├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .hz ├── LICENSE ├── biz ├── dal │ ├── init.go │ ├── mysql │ │ ├── blog.go │ │ ├── comment.go │ │ ├── follow.go │ │ ├── init.go │ │ ├── shop.go │ │ ├── user.go │ │ └── voucher.go │ └── redis │ │ ├── blog.go │ │ ├── id_worker.go │ │ ├── id_worker_test.go │ │ ├── init.go │ │ ├── lock.go │ │ ├── message.go │ │ ├── redis_test.go │ │ └── shop.go ├── handler │ ├── blog │ │ ├── blog_service.go │ │ └── blog_service_test.go │ ├── blog_comment │ │ ├── comment_service.go │ │ └── comment_service_test.go │ ├── follow │ │ ├── follow_service.go │ │ └── follow_service_test.go │ ├── image │ │ ├── image_service.go │ │ └── image_service_test.go │ ├── message │ │ ├── message_service.go │ │ └── message_service_test.go │ ├── ping.go │ ├── shop │ │ ├── shop_service.go │ │ └── shop_service_test.go │ ├── user │ │ ├── user_service.go │ │ └── user_service_test.go │ ├── voucher │ │ ├── voucher_service.go │ │ └── voucher_service_test.go │ └── xzdp │ │ ├── hello_service.go │ │ └── hello_service_test.go ├── middleware │ └── interceptor │ │ ├── cors.go │ │ ├── hll.go │ │ └── refresh_token.go ├── model │ ├── blog │ │ ├── blog.go │ │ └── table.go │ ├── blog_comment │ │ └── blog_comment.go │ ├── cache │ │ └── redis_data.go │ ├── follow │ │ └── follow.go │ ├── image │ │ └── image.go │ ├── message │ │ └── message.go │ ├── shop │ │ ├── shop.go │ │ └── shop_mapper.go │ ├── user │ │ ├── mapper.go │ │ └── user.go │ ├── voucher │ │ └── voucher.go │ └── xzdp │ │ ├── response.go │ │ └── xzdp.go ├── pkg │ ├── cache │ │ └── user.go │ └── constants │ │ └── constants.go ├── router │ ├── blog │ │ ├── blog.go │ │ └── middleware.go │ ├── blog_comment │ │ ├── blog_comment.go │ │ └── middleware.go │ ├── follow │ │ ├── follow.go │ │ └── middleware.go │ ├── image │ │ ├── image.go │ │ └── middleware.go │ ├── message │ │ ├── message.go │ │ └── middleware.go │ ├── register.go │ ├── shop │ │ ├── middleware.go │ │ └── shop.go │ ├── user │ │ ├── middleware.go │ │ └── user.go │ ├── voucher │ │ ├── middleware.go │ │ └── voucher.go │ └── xzdp │ │ ├── middleware.go │ │ └── xzdp.go ├── service │ ├── blog │ │ ├── blog_of_me.go │ │ ├── blog_of_me_test.go │ │ ├── delete_blog.go │ │ ├── delete_blog_test.go │ │ ├── get_blog.go │ │ ├── get_blog_test.go │ │ ├── get_follow_blog.go │ │ ├── get_follow_blog_test.go │ │ ├── get_hot_blog.go │ │ ├── get_hot_blog_test.go │ │ ├── get_likes.go │ │ ├── get_likes_test.go │ │ ├── get_user_blog.go │ │ ├── get_user_blog_test.go │ │ ├── like_blog.go │ │ ├── like_blog_test.go │ │ ├── post_blog.go │ │ └── post_blog_test.go │ ├── blog_comment │ │ ├── delete_comment.go │ │ ├── delete_comment_test.go │ │ ├── get_comment.go │ │ ├── get_comment_test.go │ │ ├── get_hot_comment.go │ │ ├── get_hot_comment_test.go │ │ ├── like_comment.go │ │ ├── like_comment_test.go │ │ ├── post_comment.go │ │ └── post_comment_test.go │ ├── follow │ │ ├── common_follow.go │ │ ├── common_follow_test.go │ │ ├── follow.go │ │ ├── follow_test.go │ │ ├── is_followed.go │ │ └── is_followed_test.go │ ├── image │ │ ├── upload.go │ │ └── upload_test.go │ ├── message │ │ ├── sse.go │ │ └── sse_test.go │ ├── shop │ │ ├── shop_info.go │ │ ├── shop_info_test.go │ │ ├── shop_list.go │ │ ├── shop_list_test.go │ │ ├── shop_of_type.go │ │ ├── shop_of_type_geo.go │ │ ├── shop_of_type_geo_test.go │ │ └── shop_of_type_test.go │ ├── user │ │ ├── send_code.go │ │ ├── send_code_test.go │ │ ├── user_info.go │ │ ├── user_info_test.go │ │ ├── user_login.go │ │ ├── user_login_test.go │ │ ├── user_me.go │ │ ├── user_me_test.go │ │ ├── user_sign.go │ │ ├── user_sign_count.go │ │ ├── user_sign_count_test.go │ │ └── user_sign_test.go │ ├── voucher │ │ ├── seckill_voucher.go │ │ ├── seckill_voucher_test.go │ │ ├── voucher_list.go │ │ └── voucher_list_test.go │ └── xzdp │ │ ├── hello_method.go │ │ └── hello_method_test.go └── utils │ ├── directory.go │ ├── error.go │ ├── random.go │ ├── resp.go │ ├── string_utils.go │ ├── struct.go │ ├── user.go │ └── user_holder.go ├── build.sh ├── conf ├── conf.go ├── dev │ └── conf.example.yaml ├── online │ └── conf.example.yaml └── test │ └── conf.example.yaml ├── docs └── xzdp.md ├── go.mod ├── go.sum ├── idl ├── blog.thrift ├── blog_comment.thrift ├── follow.thrift ├── image.thrift ├── message.thrift ├── shop.thrift ├── user.thrift ├── voucher.thrift └── xzdp.thrift ├── main.go ├── makefile ├── readme.md ├── resources ├── nginx-1.18.0.zip └── xzdp.sql ├── router.go ├── router_gen.go ├── script ├── bootstrap.sh ├── build.sh └── test.sh └── template ├── init.yaml ├── layout.yaml └── package.yaml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: xzdp CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | fmt: 11 | name: Run go fmt 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.22 20 | 21 | - name: Run go fmt 22 | run: | 23 | fmt_output=$(go fmt ./...) 24 | if [ -n "$fmt_output" ]; then 25 | echo "The following files need to be formatted:" 26 | echo "$fmt_output" 27 | exit 1 28 | fi 29 | 30 | build: 31 | name: Build 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v2 35 | 36 | - name: Set up Go 37 | uses: actions/setup-go@v3 38 | with: 39 | go-version: 1.22 40 | 41 | - name: Install dependencies 42 | run: go mod tidy 43 | 44 | - name: Build 45 | run: ./script/build.sh 46 | 47 | test: 48 | name: Run tests 49 | runs-on: ubuntu-latest 50 | needs: build 51 | services: 52 | redis: 53 | image: redis:latest 54 | ports: 55 | - 6379:6379 56 | 57 | mysql: 58 | image: mysql:latest 59 | env: 60 | MYSQL_ROOT_PASSWORD: password 61 | MYSQL_DATABASE: xzdp 62 | ports: 63 | - 3306:3306 64 | options: >- 65 | --health-cmd="mysqladmin ping --silent" 66 | --health-interval=10s 67 | --health-timeout=5s 68 | --health-retries=3 69 | 70 | steps: 71 | - uses: actions/checkout@v2 72 | 73 | - name: Set up Go 74 | uses: actions/setup-go@v3 75 | with: 76 | go-version: 1.22 77 | 78 | - name: Install dependencies 79 | run: go mod tidy 80 | 81 | - name: Wait for MySQL to be ready 82 | run: | 83 | until mysqladmin ping -h 127.0.0.1 --silent; do 84 | echo "Waiting for MySQL..." 85 | sleep 2 86 | done 87 | 88 | - name: Import SQL file into MySQL 89 | run: | 90 | mysql -h 127.0.0.1 -P 3306 -u root -ppassword xzdp < resources/xzdp.sql 91 | 92 | - name: Run tests 93 | env: 94 | REDIS_HOST: 127.0.0.1 95 | REDIS_PORT: 6379 96 | MYSQL_HOST: 127.0.0.1 97 | MYSQL_PORT: 3306 98 | MYSQL_USER: root 99 | MYSQL_PASSWORD: password 100 | MYSQL_DATABASE: xzdp 101 | run: ./script/test.sh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | _obj 5 | _test 6 | *.[568vq] 7 | [568vq].out 8 | *.cgo1.go 9 | *.cgo2.c 10 | _cgo_defun.c 11 | _cgo_gotypes.go 12 | _cgo_export.* 13 | _testmain.go 14 | *.exe 15 | *.exe~ 16 | *.test 17 | *.prof 18 | *.rar 19 | *.gz 20 | *.psd 21 | *.bmd 22 | *.cfg 23 | *.pptx 24 | *.log 25 | *nohup.out 26 | *settings.pyc 27 | *.sublime-project 28 | *.sublime-workspace 29 | !.gitkeep 30 | .DS_Store 31 | /.idea 32 | /.vscode 33 | /output 34 | *.local.yml 35 | conf.yaml 36 | dumped_hertz_remote_config.json 37 | conf/test/conf.yaml 38 | conf/dev/conf.yaml 39 | conf/online/conf.yaml 40 | .hz 41 | resources/nginx-1.18.0 42 | upload/ 43 | -------------------------------------------------------------------------------- /.hz: -------------------------------------------------------------------------------- 1 | // Code generated by hz. DO NOT EDIT. 2 | 3 | hz version: v0.9.1 4 | handlerDir: "" 5 | modelDir: "" 6 | routerDir: "" 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 haopengliu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /biz/dal/init.go: -------------------------------------------------------------------------------- 1 | package dal 2 | 3 | import ( 4 | "xzdp/biz/dal/mysql" 5 | "xzdp/biz/dal/redis" 6 | ) 7 | 8 | func Init() { 9 | redis.Init() 10 | mysql.Init() 11 | } 12 | -------------------------------------------------------------------------------- /biz/dal/mysql/blog.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "xzdp/biz/model/blog" 6 | "xzdp/biz/model/user" 7 | "xzdp/biz/pkg/constants" 8 | 9 | "github.com/cloudwego/hertz/pkg/common/hlog" 10 | ) 11 | 12 | func QueryBlogByUserID(ctx context.Context, current int, user *user.UserDTO) (resp []*blog.Blog, err error) { 13 | var blogs []*blog.Blog 14 | pageSize := constants.MAX_PAGE_SIZE 15 | err = DB.Where("user_id = ?", user.ID).Order("liked desc").Limit(pageSize).Offset((current - 1) * pageSize).Find(&blogs).Error 16 | if err != nil { 17 | hlog.CtxErrorf(ctx, "query error: %v", err) 18 | return nil, err 19 | } 20 | for i := range blogs { 21 | blogs[i].NickName = user.NickName 22 | blogs[i].Icon = user.Icon 23 | } 24 | 25 | return blogs, nil 26 | } 27 | 28 | func QueryHotBlog(ctx context.Context, current int) (resp []*blog.Blog, err error) { 29 | var blogs []*blog.Blog 30 | pageSize := constants.MAX_PAGE_SIZE 31 | 32 | if err := DB.Order("liked desc").Limit(pageSize).Offset((current - 1) * pageSize).Find(&blogs).Error; err != nil { 33 | hlog.CtxErrorf(ctx, "err = %s", err.Error()) 34 | return nil, err 35 | } 36 | 37 | for i := range blogs { 38 | u, err := GetById(ctx, blogs[i].UserId) 39 | if err != nil { 40 | hlog.CtxErrorf(ctx, "err = %s", err.Error()) 41 | 42 | return nil, err 43 | } 44 | if err := DB.First(&u, blogs[i].UserId).Error; err != nil { 45 | hlog.CtxErrorf(ctx, "err = %s", err.Error()) 46 | 47 | return nil, err 48 | } 49 | blogs[i].NickName = u.NickName 50 | blogs[i].Icon = u.Icon 51 | } 52 | 53 | return blogs, nil 54 | } 55 | -------------------------------------------------------------------------------- /biz/dal/mysql/comment.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "xzdp/biz/model/blog_comment" 6 | ) 7 | 8 | func DeleteBlogComment(ctx context.Context, blogID int64) error { 9 | err := DB.WithContext(ctx).Where("blog_id = ?", blogID).Delete(&blog_comment.BlogComment{}).Error 10 | return err 11 | } 12 | -------------------------------------------------------------------------------- /biz/dal/mysql/follow.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "gorm.io/gorm" 7 | follow "xzdp/biz/model/follow" 8 | user "xzdp/biz/model/user" 9 | ) 10 | 11 | func GetFansByID(ctx context.Context, id int64) (resp []*user.UserDTO, err error) { 12 | var fs []follow.Follow 13 | err = DB.Where("follow_user_id = ?", id).Find(&fs).Error 14 | if errors.Is(err, gorm.ErrRecordNotFound) { 15 | return make([]*user.UserDTO, 0), nil 16 | } 17 | if err != nil { 18 | return nil, errors.New("获取失败") 19 | } 20 | for _, f := range fs { 21 | interUser, e := GetById(ctx, f.UserId) 22 | if e != nil { 23 | return nil, e 24 | } 25 | u := &user.UserDTO{ 26 | ID: interUser.ID, 27 | NickName: interUser.NickName, 28 | Icon: interUser.Icon, 29 | } 30 | resp = append(resp, u) 31 | } 32 | return resp, nil 33 | } 34 | -------------------------------------------------------------------------------- /biz/dal/mysql/init.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "gorm.io/driver/mysql" 6 | "gorm.io/gorm" 7 | "gorm.io/gorm/schema" 8 | "time" 9 | "xzdp/conf" 10 | ) 11 | 12 | var ( 13 | DB *gorm.DB 14 | err error 15 | ) 16 | 17 | func Init() { 18 | DB, err = gorm.Open(mysql.Open(conf.GetConf().MySQL.DSN), 19 | &gorm.Config{ 20 | PrepareStmt: true, 21 | SkipDefaultTransaction: true, 22 | NamingStrategy: schema.NamingStrategy{ 23 | SingularTable: true, 24 | TablePrefix: "tb_", 25 | }, 26 | }, 27 | ) 28 | //全局注册创建时间 29 | DB.Callback().Create().Before("gorm:create").Register("update_timestamps_before_create", func(tx *gorm.DB) { 30 | if tx.Error == nil { 31 | now := time.Now().Format("2006-01-02 15:04:05") 32 | if tx.Statement.Schema != nil { 33 | if createTimeField := tx.Statement.Schema.LookUpField("create_time"); createTimeField != nil { 34 | createTimeField.Set(context.Background(), tx.Statement.ReflectValue, now) 35 | } 36 | if createTimeField := tx.Statement.Schema.LookUpField("update_time"); createTimeField != nil { 37 | createTimeField.Set(context.Background(), tx.Statement.ReflectValue, now) 38 | } 39 | } 40 | } 41 | }) 42 | //全局注册更新时间 43 | DB.Callback().Update().Before("gorm:update").Register("update_timestamps_before_update", func(tx *gorm.DB) { 44 | if tx.Error == nil { 45 | now := time.Now().Format("2006-01-02 15:04:05") 46 | if tx.Statement.Schema != nil { 47 | if updateTimeField := tx.Statement.Schema.LookUpField("update_time"); updateTimeField != nil { 48 | _ = updateTimeField.Set(context.Background(), tx.Statement.ReflectValue, now) 49 | } 50 | } 51 | } 52 | }) 53 | if err != nil { 54 | panic(err) 55 | } 56 | //autoMigrateTable() 57 | } 58 | 59 | //func autoMigrateTable() { 60 | // DB.AutoMigrate( 61 | // &model.User{}, 62 | // ) 63 | //} 64 | -------------------------------------------------------------------------------- /biz/dal/mysql/shop.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "time" 8 | "xzdp/biz/dal/redis" 9 | "xzdp/biz/model/shop" 10 | "xzdp/biz/pkg/constants" 11 | 12 | redis2 "github.com/go-redis/redis/v8" 13 | "gorm.io/gorm" 14 | ) 15 | 16 | func QueryShopType(ctx context.Context) ([]*shop.ShopType, error) { 17 | var shopTypeList []*shop.ShopType 18 | 19 | err = DB.WithContext(ctx).Order("sort asc").Find(&shopTypeList).Error 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | if len(shopTypeList) == 0 { 25 | return nil, fmt.Errorf("no exist shop type") 26 | } 27 | 28 | return shopTypeList, nil 29 | } 30 | 31 | func QueryByID(ctx context.Context, id int64) (*shop.Shop, error) { 32 | var shop shop.Shop 33 | err = DB.WithContext(ctx).First(&shop, id).Error 34 | 35 | if err != nil { 36 | return nil, err 37 | } 38 | if shop.ID == 0 { 39 | return nil, fmt.Errorf("shop isn't exist") 40 | } 41 | 42 | return &shop, nil 43 | } 44 | 45 | func QueryAllShop(ctx context.Context, shops *[]*shop.Shop) (*[]*shop.Shop, error) { 46 | for i := range *shops { 47 | dist := (*shops)[i].Distance 48 | shop, err := QueryByID(ctx, (*shops)[i].ID) 49 | if err != nil { 50 | return nil, err 51 | } 52 | shop.Distance = dist 53 | (*shops)[i] = shop 54 | 55 | } 56 | return shops, nil 57 | } 58 | 59 | func queryByID1(ctx context.Context, id int64) (*shop.Shop, error) { 60 | key := fmt.Sprintf("%s%d", constants.CACHE_SHOP_KEY, id) 61 | 62 | // Query cache from Redis 63 | shopJson, err := redis.RedisClient.Get(ctx, key).Result() 64 | if err == nil && shopJson != "" { 65 | var shop shop.Shop 66 | if err := json.Unmarshal([]byte(shopJson), &shop); err != nil { 67 | return nil, err 68 | } 69 | return &shop, nil 70 | } 71 | 72 | if err != redis2.Nil { 73 | return nil, err 74 | } 75 | 76 | // Query database 77 | var shop shop.Shop 78 | if err := DB.First(&shop, id).Error; err != nil { 79 | if err == gorm.ErrRecordNotFound { 80 | redis.RedisClient.Set(ctx, key, "", constants.CACHE_NULL_TTL).Err() 81 | } 82 | return nil, err 83 | } 84 | 85 | // Cache the result 86 | shopJsonByte, err := json.Marshal(shop) 87 | shopJson = string(shopJsonByte) 88 | if err != nil { 89 | return nil, err 90 | } 91 | redis.RedisClient.Set(ctx, key, shopJson, constants.CACHE_SHOP_TTL).Err() 92 | 93 | return &shop, nil 94 | } 95 | func queryByID2(ctx context.Context, id int64) (*shop.Shop, error) { 96 | key := fmt.Sprintf("%s%d", constants.CACHE_SHOP_KEY, id) 97 | lockKey := fmt.Sprintf("%s%d", constants.LOCK_SHOP_KEY, id) 98 | 99 | // 1. 从 Redis 获取数据 100 | result, err := redis.GetShopFromCache(ctx, key) 101 | if err == nil { 102 | return result, nil 103 | } 104 | lock := redis.NewLock(ctx, lockKey, key, 10) 105 | // 2. 缓存未命中,尝试获取锁 106 | isLocked := lock.TryLock() 107 | defer lock.UnLock(key) 108 | if !isLocked { 109 | // 锁获取失败,等待后重试 110 | time.Sleep(50 * time.Millisecond) 111 | return queryByID2(ctx, id) 112 | } 113 | 114 | // 2.2 获取锁成功,再次检查缓存 115 | result, err = redis.GetShopFromCache(ctx, key) 116 | if err == nil { 117 | return result, nil 118 | } 119 | 120 | // 3. 从数据库查询数据 121 | var shop shop.Shop 122 | if err := DB.First(&shop, id).Error; err != nil { 123 | if err == gorm.ErrRecordNotFound { 124 | redis.RedisClient.Set(ctx, key, "", constants.CACHE_NULL_TTL).Err() 125 | return nil, err 126 | } 127 | return nil, err 128 | } 129 | 130 | // 4. 数据库中存在,缓存数据 131 | shopJson, err := json.Marshal(shop) 132 | if err != nil { 133 | return nil, err 134 | } 135 | redis.RedisClient.Set(ctx, key, string(shopJson), constants.CACHE_SHOP_TTL).Err() 136 | return &shop, nil 137 | } 138 | 139 | func LoadShopListToCache(ctx context.Context) error { 140 | shopMap := make(map[int64][]shop.Shop) 141 | var shops []*shop.Shop 142 | err := DB.Find(&shops).Error 143 | if err != nil { 144 | return err 145 | } 146 | for _, v := range shops { 147 | shopMap[v.TypeId] = append(shopMap[v.TypeId], *v) 148 | } 149 | for typeId, shops := range shopMap { 150 | key := constants.SHOP_GEO_KEY + fmt.Sprint(typeId) 151 | 152 | var locations []*redis2.GeoLocation 153 | for _, shop := range shops { 154 | location := &redis2.GeoLocation{ 155 | Name: fmt.Sprint(shop.ID), 156 | Longitude: shop.X, 157 | Latitude: shop.Y, 158 | } 159 | locations = append(locations, location) 160 | } 161 | 162 | _, err := redis.RedisClient.GeoAdd(ctx, key, locations...).Result() 163 | if err != nil { 164 | return err 165 | } 166 | } 167 | return nil 168 | } 169 | -------------------------------------------------------------------------------- /biz/dal/mysql/user.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | model "xzdp/biz/model/user" 7 | "xzdp/biz/utils" 8 | 9 | "gorm.io/gorm" 10 | ) 11 | 12 | func GetById(ctx context.Context, id int64) (*model.User, error) { 13 | 14 | var user model.User 15 | db := DB.Model(&model.User{}) 16 | 17 | // Perform the query 18 | if err := db.First(&user, "id = ?", id).Error; err != nil { 19 | if errors.Is(err, gorm.ErrRecordNotFound) { 20 | // Handle case where no user is found 21 | return nil, utils.ErrNotFound 22 | } 23 | // Handle other potential errors 24 | return nil, err 25 | } 26 | 27 | // Return the user 28 | return &user, nil 29 | } 30 | 31 | func GetUserInfoById(ctx context.Context, id int64) (*model.UserInfo, error) { 32 | 33 | var user model.UserInfo 34 | db := DB.Model(&model.UserInfo{}) 35 | 36 | // Perform the query 37 | if err := db.First(&user, "user_id = ?", id).Error; err != nil { 38 | if err == gorm.ErrRecordNotFound { 39 | // Handle case where no user is found 40 | return nil, utils.ErrNotFound 41 | } 42 | // Handle other potential errors 43 | return nil, err 44 | } 45 | 46 | // Return the user 47 | return &user, nil 48 | } 49 | -------------------------------------------------------------------------------- /biz/dal/mysql/voucher.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "xzdp/biz/model/voucher" 7 | 8 | "gorm.io/gorm" 9 | ) 10 | 11 | func QueryShopVoucherByShopID(ctx context.Context, id int64) ([]*voucher.SeckillVoucher, error) { 12 | var voucherList []*voucher.SeckillVoucher 13 | 14 | err = DB.WithContext(ctx).Raw("select v.*,s.stock,s.begin_time,s.end_time from tb_voucher v left join tb_seckill_voucher s on s.voucher_id = v.id where v.shop_id = ?", id). 15 | Find(&voucherList).Error 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return voucherList, nil 21 | } 22 | 23 | func QueryVoucherByID(ctx context.Context, id int64) (*voucher.SeckillVoucher, error) { 24 | var seckillVoucher voucher.SeckillVoucher 25 | err = DB.WithContext(ctx).Where("voucher_id = ?", id).Order("create_time desc").Limit(1).Find(&seckillVoucher).Error 26 | return &seckillVoucher, err 27 | } 28 | 29 | func QueryVoucherOrderByVoucherID(ctx context.Context, userId int64, id int64) error { 30 | var voucherOrder voucher.VoucherOrder 31 | err = DB.WithContext(ctx).Where("voucher_id = ? and user_id=?", id, userId).Limit(1).Find(&voucherOrder).Error 32 | if err != nil { 33 | if !errors.Is(err, gorm.ErrRecordNotFound) { 34 | return errors.New("重复下单") 35 | } 36 | } else if voucherOrder.ID != 0 { 37 | return errors.New("重复下单") 38 | } 39 | return nil 40 | } 41 | 42 | func UpdateVoucherStock(ctx context.Context, id int64) error { 43 | result := DB.WithContext(ctx).Model(&voucher.SeckillVoucher{}). 44 | Where("voucher_id = ? and stock > 0", id). 45 | Update("stock", gorm.Expr("stock - ?", 1)) 46 | if result.RowsAffected == 0 { 47 | return errors.New("库存不足,扣减失败") 48 | } 49 | return result.Error 50 | } 51 | 52 | func CreateVoucherOrder(ctx context.Context, voucherOrder *voucher.VoucherOrder) error { 53 | return DB.WithContext(ctx).Create(voucherOrder).Error 54 | } 55 | -------------------------------------------------------------------------------- /biz/dal/redis/blog.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | redis2 "github.com/go-redis/redis/v8" 7 | "xzdp/biz/pkg/constants" 8 | ) 9 | 10 | func IsLiked(ctx context.Context, key string, member string) (ok bool, err error) { 11 | _, err = RedisClient.ZScore(ctx, key, member).Result() 12 | if err != nil { 13 | if errors.Is(err, redis2.Nil) { 14 | return false, nil 15 | } 16 | return false, err 17 | } 18 | return true, nil 19 | } 20 | 21 | func HasLikes(ctx context.Context, key string) (bool, error) { 22 | exists, err := RedisClient.Exists(ctx, key).Result() 23 | if err != nil { 24 | return false, err 25 | } 26 | return exists > 0, nil 27 | } 28 | 29 | func DeleteLikes(ctx context.Context, key string) error { 30 | err := RedisClient.Del(ctx, key).Err() 31 | if err != nil { 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | func GetBlogsByKey(ctx context.Context, key string, max string, offset int64) ([]redis2.Z, error) { 38 | size := constants.MAX_PAGE_SIZE 39 | result, err := RedisClient.ZRevRangeByScoreWithScores( 40 | ctx, 41 | key, 42 | &redis2.ZRangeBy{Max: max, Min: "0", Offset: offset, Count: int64(size)}, 43 | ).Result() 44 | if err != nil { 45 | return nil, err 46 | } 47 | return result, nil 48 | } 49 | -------------------------------------------------------------------------------- /biz/dal/redis/id_worker.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "time" 6 | "xzdp/biz/pkg/constants" 7 | ) 8 | 9 | func NextId(ctx context.Context, prefix string) (int64, error) { 10 | beginTimeStamp := int64(constants.BEGIN_TIMESTAMP) 11 | now := time.Now().Unix() 12 | timeStamp := now - beginTimeStamp 13 | dateKey := time.Unix(now, 0).Format("2006-01-02") 14 | count, err := RedisClient.Incr(ctx, constants.ICRID_KEY+prefix+":"+dateKey).Result() 15 | if err != nil { 16 | return 0, err 17 | } 18 | return (timeStamp << constants.COUNT_BIT) | count, nil 19 | } 20 | -------------------------------------------------------------------------------- /biz/dal/redis/id_worker_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | func TestNextId(t *testing.T) { 10 | Init() 11 | 12 | // 测试用例 13 | testCases := []struct { 14 | name string 15 | prefix string 16 | wantErr bool 17 | }{ 18 | {"valid_prefix", "test", false}, 19 | } 20 | 21 | for _, tc := range testCases { 22 | t.Run(tc.name, func(t *testing.T) { 23 | var idSet sync.Map // 使用sync.Map来存储ID 24 | var wg sync.WaitGroup 25 | numGoroutines := 1000 26 | 27 | // 启动100个协程 28 | for i := 0; i < numGoroutines; i++ { 29 | wg.Add(1) 30 | go func() { 31 | defer wg.Done() 32 | for i := 0; i < 500; i++ { 33 | id, err := NextId(context.Background(), tc.prefix) 34 | if err != nil { 35 | t.Errorf("NextId() returned error: %v", err) 36 | return 37 | } 38 | if id <= 0 { 39 | t.Errorf("NextId() returned invalid ID: %d", id) 40 | } 41 | idSet.Store(id, struct{}{}) // 将ID放入sync.Map中 42 | } 43 | }() 44 | } 45 | 46 | // 等待所有协程完成 47 | wg.Wait() 48 | 49 | // 检查生成的ID数量是否为100 50 | var uniqueCount int 51 | idSet.Range(func(key, value interface{}) bool { 52 | uniqueCount++ 53 | return true // 返回true继续遍历 54 | }) 55 | t.Log("uniqueCount:", uniqueCount) 56 | if uniqueCount != numGoroutines*500 { 57 | t.Errorf("Expected %d unique IDs, got %d", numGoroutines, uniqueCount) 58 | } 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /biz/dal/redis/init.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "github.com/go-redis/redis/v8" 8 | "github.com/go-redsync/redsync/v4" 9 | "github.com/go-redsync/redsync/v4/redis/goredis/v8" 10 | "time" 11 | "xzdp/biz/model/cache" 12 | "xzdp/biz/pkg/constants" 13 | "xzdp/conf" 14 | ) 15 | 16 | var ( 17 | RedisClient *redis.Client 18 | RedsyncClient *redsync.Redsync 19 | ) 20 | 21 | type ArgsFunc func(args ...interface{}) (interface{}, error) 22 | 23 | func Init() { 24 | RedisClient = redis.NewClient(&redis.Options{ 25 | Addr: conf.GetConf().Redis.Address, 26 | Password: conf.GetConf().Redis.Password, 27 | }) 28 | if err := RedisClient.Ping(context.Background()).Err(); err != nil { 29 | panic(err) 30 | } 31 | pool := goredis.NewPool(RedisClient) 32 | RedsyncClient = redsync.New(pool) 33 | } 34 | 35 | func SetString(ctx context.Context, key string, value interface{}, duration time.Duration) error { 36 | jsonData, err := json.Marshal(value) 37 | if err != nil { 38 | return err 39 | } 40 | err = RedisClient.Set(ctx, key, jsonData, duration).Err() 41 | if err != nil { 42 | return err 43 | } 44 | return nil 45 | } 46 | 47 | func SetStringLogical(ctx context.Context, key string, value interface{}, duration time.Duration) error { 48 | stringValue, err := json.Marshal(value) 49 | cacheData := cache.NewRedisStringData(string(stringValue), time.Now().Add(duration)) 50 | jsonData, err := json.Marshal(*cacheData) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | err = RedisClient.Set(ctx, key, jsonData, 0).Err() 56 | if err != nil { 57 | return err 58 | } 59 | 60 | return nil 61 | } 62 | 63 | //func GetString(ctx context.Context, key string, duration time.Duration) (interface{}, error) { 64 | // 65 | //} 66 | 67 | func GetStringLogical(ctx context.Context, key string, duration time.Duration, dbFallback ArgsFunc, args ...interface{}) (string, error) { 68 | redisJson, err := RedisClient.Get(ctx, key).Result() 69 | lock := NewLock(ctx, constants.LOCK_KEY+key, "lock", duration) 70 | if redisJson == "" || errors.Is(err, redis.Nil) { 71 | if lock.TryLock() { 72 | go func() { 73 | defer lock.UnLock("lock") 74 | data, err := dbFallback(args...) 75 | if err != nil { 76 | return 77 | } 78 | err = SetStringLogical(ctx, key, data, duration) 79 | if err != nil { 80 | return 81 | } 82 | }() 83 | } 84 | } else { 85 | var redisData cache.RedisStringData 86 | if err := json.Unmarshal([]byte(redisJson), &redisData); err != nil { 87 | return "", err 88 | } 89 | if redisData.ExpiredTime.After(time.Now()) { 90 | return redisData.Data, nil 91 | } else { 92 | if lock.TryLock() { 93 | go func() { 94 | defer lock.UnLock("lock") 95 | data, err := dbFallback(args...) 96 | if err != nil { 97 | return 98 | } 99 | err = SetStringLogical(ctx, key, data, duration) 100 | if err != nil { 101 | return 102 | } 103 | }() 104 | } 105 | return redisData.Data, nil 106 | } 107 | } 108 | return "", nil 109 | } 110 | -------------------------------------------------------------------------------- /biz/dal/redis/lock.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | ) 8 | 9 | type Lock struct { 10 | Ctx context.Context 11 | Key string 12 | Value string 13 | Expire time.Duration 14 | } 15 | 16 | func NewLock(ctx context.Context, key string, value string, expire time.Duration) *Lock { 17 | return &Lock{ 18 | Ctx: ctx, 19 | Key: key, 20 | Value: value, 21 | Expire: expire, 22 | } 23 | } 24 | 25 | func (l *Lock) TryLock() bool { 26 | // value 应当全局唯一 27 | success, err := RedisClient.SetNX(l.Ctx, l.Key, l.Value, l.Expire*time.Second).Result() 28 | if err != nil { 29 | log.Printf("Error acquiring lock: %v", err) 30 | return false 31 | } 32 | return success 33 | } 34 | 35 | func (l *Lock) UnLock(value string) { 36 | luaScript := ` 37 | if redis.call("get", KEYS[1]) == ARGV[1] then 38 | return redis.call("del", KEYS[1]) 39 | end 40 | return 0 41 | ` 42 | RedisClient.Eval(l.Ctx, luaScript, []string{l.Key}, value) 43 | } 44 | -------------------------------------------------------------------------------- /biz/dal/redis/message.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "github.com/go-redis/redis/v8" 6 | "time" 7 | "xzdp/biz/pkg/constants" 8 | "xzdp/biz/utils" 9 | ) 10 | 11 | // ProduceMq 写stream消息队列 12 | func ProduceMq(ctx context.Context, key string, message interface{}) error { 13 | messageJSON, err := utils.SerializeStruct(message) 14 | if err != nil { 15 | return err 16 | } 17 | // xadd写stream 18 | err = RedisClient.XAdd(ctx, &redis.XAddArgs{ 19 | Stream: key, 20 | ID: "*", 21 | Values: []interface{}{"message", messageJSON}, 22 | }).Err() 23 | // 错误处理 24 | if err != nil { 25 | return err 26 | } 27 | return nil 28 | } 29 | 30 | // ConsumeMq 读取stream消息队列 31 | func ConsumeMq(ctx context.Context, key string, consumer string, block time.Duration, count int64, id string) ([]redis.XStream, error) { 32 | if id == "" { 33 | id = ">" 34 | } 35 | xSet, err := RedisClient.XReadGroup(ctx, &redis.XReadGroupArgs{ 36 | Group: constants.STREAM_READ_GROUP, 37 | Consumer: consumer, 38 | Streams: []string{key, id}, 39 | Count: count, 40 | Block: block, 41 | NoAck: false, 42 | }).Result() 43 | if err != nil { 44 | return nil, err 45 | } 46 | return xSet, nil 47 | } 48 | func CreateConsumerGroup(ctx context.Context, key string) { 49 | RedisClient.XGroupCreateMkStream(ctx, key, constants.STREAM_READ_GROUP, "0") 50 | } 51 | func AckMq(ctx context.Context, key string, id string) error { 52 | return RedisClient.XAck(ctx, key, constants.STREAM_READ_GROUP, id).Err() 53 | } 54 | -------------------------------------------------------------------------------- /biz/dal/redis/redis_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math" 8 | "testing" 9 | 10 | "github.com/cloudwego/hertz/pkg/common/test/assert" 11 | ) 12 | 13 | func TestHyperLogLog(t *testing.T) { 14 | Init() 15 | values := make([]interface{}, 1000) 16 | ctx := context.Background() 17 | var total int64 = 1000000 18 | // 批量保存 100w 条用户记录,每批 1000 条 19 | var i int64 20 | for i = 0; i < total; i++ { 21 | // 获取当前批次的索引 22 | j := i % 1000 23 | // 生成用户记录 24 | values[j] = "user_" + fmt.Sprint(i) 25 | // 每 1000 条记录发送一次到 Redis 26 | if j == 999 { 27 | err := RedisClient.PFAdd(ctx, "hl2", values...).Err() 28 | if err != nil { 29 | log.Fatalf("Failed to add values to HyperLogLog: %v", err) 30 | } 31 | } 32 | } 33 | 34 | // 统计 HyperLogLog 中的用户数量 35 | count, err := RedisClient.PFCount(ctx, "hl2").Result() 36 | if err != nil { 37 | log.Fatalf("Failed to get HyperLogLog count: %v", err) 38 | } 39 | log.Printf("HyperLogLog count: %d", count) 40 | diff := math.Abs(float64(total - count)) 41 | assert.True(t, count > 0) 42 | assert.True(t, diff < float64(total)/100) 43 | } 44 | -------------------------------------------------------------------------------- /biz/dal/redis/shop.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "strconv" 9 | "xzdp/biz/model/shop" 10 | "xzdp/biz/pkg/constants" 11 | 12 | "github.com/go-redis/redis/v8" 13 | ) 14 | 15 | func GetShopFromCache(ctx context.Context, key string) (*shop.Shop, error) { 16 | shopJson, err := RedisClient.Get(ctx, key).Result() 17 | if err == nil && shopJson != "" { 18 | var shop shop.Shop 19 | if err := json.Unmarshal([]byte(shopJson), &shop); err != nil { 20 | return nil, err 21 | } 22 | return &shop, nil 23 | } 24 | 25 | if err != redis.Nil { 26 | return nil, err 27 | } 28 | 29 | if shopJson == "" { 30 | return nil, errors.New("shop not found in cache") 31 | } 32 | 33 | return nil, errors.New("unknown error") 34 | } 35 | 36 | func QueryShopWithDistance(ctx context.Context, req *shop.ShopOfTypeGeoReq) (resp *[]*shop.Shop, err error) { 37 | current := req.Current 38 | from := (current - 1) * constants.DEFAULT_PAGE_SIZE 39 | end := current * constants.DEFAULT_PAGE_SIZE 40 | 41 | key := constants.SHOP_GEO_KEY + strconv.Itoa(int(req.TypeId)) 42 | geoRadiusQuery := redis.GeoRadiusQuery{ 43 | Radius: req.Distance, 44 | WithDist: true, 45 | Sort: "ASC", 46 | Count: int(end), 47 | } 48 | locations, err := RedisClient.GeoRadius(ctx, key, req.Longitude, req.Latitude, &geoRadiusQuery).Result() 49 | 50 | if err != nil { 51 | fmt.Printf("Error querying Redis: %v\n", err) 52 | return nil, err 53 | } 54 | if len(locations) == 0 { 55 | return &[]*shop.Shop{}, nil 56 | } 57 | if len(locations) <= int(from) { 58 | return &[]*shop.Shop{}, nil 59 | } 60 | 61 | shops := make([]*shop.Shop, 0, len(locations)) 62 | for _, loc := range locations[from:end] { 63 | id, _ := strconv.ParseInt(loc.Name, 10, 64) 64 | shops = append(shops, &shop.Shop{ 65 | ID: id, 66 | X: loc.Longitude, 67 | Y: loc.Latitude, 68 | Distance: loc.Dist, 69 | TypeId: int64(req.TypeId), 70 | }) 71 | } 72 | return &shops, nil 73 | } 74 | -------------------------------------------------------------------------------- /biz/handler/blog/blog_service.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | blog "xzdp/biz/model/blog" 7 | _ "xzdp/biz/model/user" 8 | service "xzdp/biz/service/blog" 9 | "xzdp/biz/utils" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | "github.com/cloudwego/hertz/pkg/protocol/consts" 13 | ) 14 | 15 | // GetHotBlog . 16 | // @router /blog/hot [GET] 17 | func GetHotBlog(ctx context.Context, c *app.RequestContext) { 18 | var err error 19 | var req blog.BlogReq 20 | err = c.BindAndValidate(&req) 21 | if err != nil { 22 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 23 | return 24 | } 25 | 26 | resp, err := service.NewGetHotBlogService(ctx, c).Run(&req) 27 | if err != nil { 28 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 29 | return 30 | } 31 | 32 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 33 | } 34 | 35 | // PostBlog . 36 | // @router /blog/post [POST] 37 | func PostBlog(ctx context.Context, c *app.RequestContext) { 38 | var err error 39 | var req blog.Blog 40 | err = c.BindAndValidate(&req) 41 | if err != nil { 42 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 43 | return 44 | } 45 | 46 | resp, err := service.NewPostBlogService(ctx, c).Run(&req) 47 | 48 | if err != nil { 49 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 50 | return 51 | } 52 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 53 | } 54 | 55 | // GetBlog . 56 | // @router /blog/:id [GET] 57 | func GetBlog(ctx context.Context, c *app.RequestContext) { 58 | var err error 59 | id := c.Param("id") 60 | if err != nil { 61 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 62 | return 63 | } 64 | 65 | resp, err := service.NewGetBlogService(ctx, c).Run(&id) 66 | 67 | if err != nil { 68 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 69 | return 70 | } 71 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 72 | } 73 | 74 | // LikeBlog . 75 | // @router /blog/like/:id [PUT] 76 | func LikeBlog(ctx context.Context, c *app.RequestContext) { 77 | var err error 78 | id := c.Param("id") 79 | resp, err := service.NewLikeBlogService(ctx, c).Run(&id) 80 | if err != nil { 81 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 82 | return 83 | } 84 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 85 | } 86 | 87 | // GetLikes . 88 | // @router /blog/likes/:id [GET] 89 | func GetLikes(ctx context.Context, c *app.RequestContext) { 90 | var err error 91 | id := c.Param("id") 92 | if err != nil { 93 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 94 | return 95 | } 96 | 97 | resp, err := service.NewGetLikesService(ctx, c).Run(&id) 98 | 99 | if err != nil { 100 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 101 | return 102 | } 103 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 104 | } 105 | 106 | // GetUserBlog . 107 | // @router /blog/user/:id [GET] 108 | func GetUserBlog(ctx context.Context, c *app.RequestContext) { 109 | var err error 110 | var req blog.BlogReq 111 | err = c.BindAndValidate(&req) 112 | id := c.Param("id") 113 | if err != nil { 114 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 115 | return 116 | } 117 | userId, err := strconv.ParseInt(id, 10, 64) 118 | if err != nil { 119 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 120 | return 121 | } 122 | resp, err := service.NewGetUserBlogService(ctx, c).Run(&req, userId) 123 | 124 | if err != nil { 125 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 126 | return 127 | } 128 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 129 | } 130 | 131 | // DeleteBlog . 132 | // @router /blog/:id [DELETE] 133 | func DeleteBlog(ctx context.Context, c *app.RequestContext) { 134 | var err error 135 | req := c.Param("id") 136 | if err != nil { 137 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 138 | return 139 | } 140 | 141 | resp, err := service.NewDeleteBlogService(ctx, c).Run(&req) 142 | 143 | if err != nil { 144 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 145 | return 146 | } 147 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 148 | } 149 | 150 | // GetFollowBlog . 151 | // @router /blog/of/follow [GET] 152 | func GetFollowBlog(ctx context.Context, c *app.RequestContext) { 153 | var err error 154 | var req blog.FollowBlogReq 155 | err = c.BindAndValidate(&req) 156 | if err != nil { 157 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 158 | return 159 | } 160 | 161 | resp, err := service.NewGetFollowBlogService(ctx, c).Run(&req) 162 | 163 | if err != nil { 164 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 165 | return 166 | } 167 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 168 | } 169 | 170 | // BlogOfMe . 171 | // @router /blog/of/me [GET] 172 | func BlogOfMe(ctx context.Context, c *app.RequestContext) { 173 | var err error 174 | var req blog.BlogReq 175 | err = c.BindAndValidate(&req) 176 | if err != nil { 177 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 178 | return 179 | } 180 | 181 | resp, err := service.NewBlogOfMeService(ctx, c).Run(&req) 182 | 183 | if err != nil { 184 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 185 | return 186 | } 187 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 188 | } 189 | -------------------------------------------------------------------------------- /biz/handler/blog/blog_service_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestGetHotBlog(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/blog/hot", GetHotBlog) 15 | w := ut.PerformRequest(h.Engine, "GET", "/blog/hot", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | 23 | func TestGetUserBlog(t *testing.T) { 24 | h := server.Default() 25 | h.GET("/blog/user/:id", GetUserBlog) 26 | w := ut.PerformRequest(h.Engine, "GET", "/blog/user/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 27 | ut.Header{}) 28 | resp := w.Result() 29 | assert.DeepEqual(t, 201, resp.StatusCode()) 30 | assert.DeepEqual(t, "", string(resp.Body())) 31 | // todo edit your unit test. 32 | } 33 | 34 | func TestBlogOfMe(t *testing.T) { 35 | h := server.Default() 36 | h.GET("/blog/of/me", BlogOfMe) 37 | w := ut.PerformRequest(h.Engine, "GET", "/blog/of/me", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 38 | ut.Header{}) 39 | resp := w.Result() 40 | assert.DeepEqual(t, 201, resp.StatusCode()) 41 | assert.DeepEqual(t, "", string(resp.Body())) 42 | // todo edit your unit test. 43 | } 44 | 45 | func TestPostBlog(t *testing.T) { 46 | h := server.Default() 47 | h.GET("/blog", PostBlog) 48 | w := ut.PerformRequest(h.Engine, "POST", "/blog", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 49 | ut.Header{}) 50 | resp := w.Result() 51 | assert.DeepEqual(t, 201, resp.StatusCode()) 52 | assert.DeepEqual(t, "", string(resp.Body())) 53 | // todo edit your unit test. 54 | } 55 | 56 | func TestGetBlog(t *testing.T) { 57 | h := server.Default() 58 | h.GET("/blog/:id", GetBlog) 59 | w := ut.PerformRequest(h.Engine, "GET", "/blog/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 60 | ut.Header{}) 61 | resp := w.Result() 62 | assert.DeepEqual(t, 201, resp.StatusCode()) 63 | assert.DeepEqual(t, "", string(resp.Body())) 64 | // todo edit your unit test. 65 | } 66 | 67 | func TestDeleteBlog(t *testing.T) { 68 | h := server.Default() 69 | h.GET("/blog/:id", DeleteBlog) 70 | w := ut.PerformRequest(h.Engine, "DELETE", "/blog/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 71 | ut.Header{}) 72 | resp := w.Result() 73 | assert.DeepEqual(t, 201, resp.StatusCode()) 74 | assert.DeepEqual(t, "", string(resp.Body())) 75 | // todo edit your unit test. 76 | } 77 | 78 | func TestLikeBlog(t *testing.T) { 79 | h := server.Default() 80 | h.GET("/blog/like/:id", LikeBlog) 81 | w := ut.PerformRequest(h.Engine, "PUT", "/blog/like/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 82 | ut.Header{}) 83 | resp := w.Result() 84 | assert.DeepEqual(t, 201, resp.StatusCode()) 85 | assert.DeepEqual(t, "", string(resp.Body())) 86 | // todo edit your unit test. 87 | } 88 | 89 | func TestGetLikes(t *testing.T) { 90 | h := server.Default() 91 | h.GET("/blog/likes/:id", GetLikes) 92 | w := ut.PerformRequest(h.Engine, "GET", "/blog/likes/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 93 | ut.Header{}) 94 | resp := w.Result() 95 | assert.DeepEqual(t, 201, resp.StatusCode()) 96 | assert.DeepEqual(t, "", string(resp.Body())) 97 | // todo edit your unit test. 98 | } 99 | 100 | func TestGetFollowBlog(t *testing.T) { 101 | h := server.Default() 102 | h.GET("/blog/of/follow", GetFollowBlog) 103 | w := ut.PerformRequest(h.Engine, "GET", "/blog/of/follow", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 104 | ut.Header{}) 105 | resp := w.Result() 106 | assert.DeepEqual(t, 201, resp.StatusCode()) 107 | assert.DeepEqual(t, "", string(resp.Body())) 108 | // todo edit your unit test. 109 | } 110 | -------------------------------------------------------------------------------- /biz/handler/blog_comment/comment_service.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | 6 | blog_comment "xzdp/biz/model/blog_comment" 7 | service "xzdp/biz/service/blog_comment" 8 | "xzdp/biz/utils" 9 | 10 | "github.com/cloudwego/hertz/pkg/app" 11 | "github.com/cloudwego/hertz/pkg/protocol/consts" 12 | ) 13 | 14 | // GetHotComment . 15 | // @router /comment/hot [GET] 16 | func GetHotComment(ctx context.Context, c *app.RequestContext) { 17 | var err error 18 | var req blog_comment.CommentReq 19 | err = c.BindAndValidate(&req) 20 | if err != nil { 21 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 22 | return 23 | } 24 | 25 | resp, err := service.NewGetHotCommentService(ctx, c).Run(&req) 26 | if err != nil { 27 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 28 | return 29 | } 30 | 31 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 32 | } 33 | 34 | // GetComment . 35 | // @router /comment/:blogID [GET] 36 | func GetComment(ctx context.Context, c *app.RequestContext) { 37 | var err error 38 | var req blog_comment.CommentReq 39 | err = c.BindAndValidate(&req) 40 | if err != nil { 41 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 42 | return 43 | } 44 | 45 | resp, err := service.NewGetCommentService(ctx, c).Run(&req) 46 | if err != nil { 47 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 48 | return 49 | } 50 | 51 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 52 | } 53 | 54 | // PostComment . 55 | // @router /comment/post [POST] 56 | func PostComment(ctx context.Context, c *app.RequestContext) { 57 | var err error 58 | var req blog_comment.BlogComment 59 | err = c.BindAndValidate(&req) 60 | if err != nil { 61 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 62 | return 63 | } 64 | 65 | resp, err := service.NewPostCommentService(ctx, c).Run(&req) 66 | if err != nil { 67 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 68 | return 69 | } 70 | 71 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 72 | } 73 | 74 | // LikeComment . 75 | // @router /comment/like/:id [PUT] 76 | func LikeComment(ctx context.Context, c *app.RequestContext) { 77 | var err error 78 | var req string 79 | err = c.BindAndValidate(&req) 80 | if err != nil { 81 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 82 | return 83 | } 84 | 85 | resp, err := service.NewLikeCommentService(ctx, c).Run(&req) 86 | if err != nil { 87 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 88 | return 89 | } 90 | 91 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 92 | } 93 | 94 | // DeleteComment . 95 | // @router /comment/:id [DELETE] 96 | func DeleteComment(ctx context.Context, c *app.RequestContext) { 97 | var err error 98 | var req string 99 | err = c.BindAndValidate(&req) 100 | if err != nil { 101 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 102 | return 103 | } 104 | 105 | resp, err := service.NewDeleteCommentService(ctx, c).Run(&req) 106 | if err != nil { 107 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 108 | return 109 | } 110 | 111 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 112 | } 113 | -------------------------------------------------------------------------------- /biz/handler/blog_comment/comment_service_test.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestGetHotComment(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/comment/hot", GetHotComment) 15 | w := ut.PerformRequest(h.Engine, "GET", "/comment/hot", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | 23 | func TestGetComment(t *testing.T) { 24 | h := server.Default() 25 | h.GET("/comment/:blogID", GetComment) 26 | w := ut.PerformRequest(h.Engine, "GET", "/comment/:blogID", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 27 | ut.Header{}) 28 | resp := w.Result() 29 | assert.DeepEqual(t, 201, resp.StatusCode()) 30 | assert.DeepEqual(t, "", string(resp.Body())) 31 | // todo edit your unit test. 32 | } 33 | 34 | func TestPostComment(t *testing.T) { 35 | h := server.Default() 36 | h.GET("/comment/post", PostComment) 37 | w := ut.PerformRequest(h.Engine, "POST", "/comment/post", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 38 | ut.Header{}) 39 | resp := w.Result() 40 | assert.DeepEqual(t, 201, resp.StatusCode()) 41 | assert.DeepEqual(t, "", string(resp.Body())) 42 | // todo edit your unit test. 43 | } 44 | 45 | func TestLikeComment(t *testing.T) { 46 | h := server.Default() 47 | h.GET("/comment/like/:id", LikeComment) 48 | w := ut.PerformRequest(h.Engine, "PUT", "/comment/like/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 49 | ut.Header{}) 50 | resp := w.Result() 51 | assert.DeepEqual(t, 201, resp.StatusCode()) 52 | assert.DeepEqual(t, "", string(resp.Body())) 53 | // todo edit your unit test. 54 | } 55 | 56 | func TestDeleteComment(t *testing.T) { 57 | h := server.Default() 58 | h.GET("/comment/:id", DeleteComment) 59 | w := ut.PerformRequest(h.Engine, "DELETE", "/comment/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 60 | ut.Header{}) 61 | resp := w.Result() 62 | assert.DeepEqual(t, 201, resp.StatusCode()) 63 | assert.DeepEqual(t, "", string(resp.Body())) 64 | // todo edit your unit test. 65 | } 66 | -------------------------------------------------------------------------------- /biz/handler/follow/follow_service.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | follow "xzdp/biz/model/follow" 8 | service "xzdp/biz/service/follow" 9 | "xzdp/biz/utils" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | "github.com/cloudwego/hertz/pkg/protocol/consts" 13 | ) 14 | 15 | // Follow . 16 | // @router /follow [GET] 17 | func Follow(ctx context.Context, c *app.RequestContext) { 18 | var err error 19 | id := c.Param("id") 20 | isFollow := c.Param("isFollow") 21 | intVal, err := strconv.ParseInt(id, 10, 64) 22 | if err != nil { 23 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 24 | return 25 | } 26 | boolVal, err := strconv.ParseBool(isFollow) 27 | if err != nil { 28 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 29 | return 30 | } 31 | req := follow.FollowReq{ 32 | TargetUser: intVal, 33 | IsFollow: boolVal, 34 | } 35 | targetUserId := req.GetTargetUser() 36 | // 如果参数不合法,直接返回 37 | if targetUserId == 0 { 38 | utils.SendErrResponse(ctx, c, consts.StatusOK, errors.New("参数不合法")) 39 | return 40 | } 41 | resp, err := service.NewFollowService(ctx, c).Run(&req) 42 | if err != nil { 43 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 44 | return 45 | } 46 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 47 | } 48 | 49 | // IsFollowed . 50 | // @router /follow/isFollowed [GET] 51 | func IsFollowed(ctx context.Context, c *app.RequestContext) { 52 | var err error 53 | id := c.Param("id") 54 | resp, err := service.NewIsFollowedService(ctx, c).Run(id) 55 | if err != nil { 56 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 57 | return 58 | } 59 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 60 | } 61 | 62 | // CommonFollow . 63 | // @router /follow/common/:id [GET] 64 | func CommonFollow(ctx context.Context, c *app.RequestContext) { 65 | var err error 66 | id := c.Param("id") 67 | resp, err := service.NewCommonFollowService(ctx, c).Run(id) 68 | 69 | if err != nil { 70 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 71 | return 72 | } 73 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 74 | } 75 | -------------------------------------------------------------------------------- /biz/handler/follow/follow_service_test.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestFollow(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/follow", Follow) 15 | w := ut.PerformRequest(h.Engine, "GET", "/follow", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | 23 | func TestIsFollowed(t *testing.T) { 24 | h := server.Default() 25 | h.GET("/follow/isFollowed", IsFollowed) 26 | w := ut.PerformRequest(h.Engine, "GET", "/follow/isFollowed", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 27 | ut.Header{}) 28 | resp := w.Result() 29 | assert.DeepEqual(t, 201, resp.StatusCode()) 30 | assert.DeepEqual(t, "", string(resp.Body())) 31 | // todo edit your unit test. 32 | } 33 | -------------------------------------------------------------------------------- /biz/handler/image/image_service.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | 10 | image "xzdp/biz/model/image" 11 | service "xzdp/biz/service/image" 12 | "xzdp/biz/utils" 13 | 14 | "github.com/cloudwego/hertz/pkg/app" 15 | "github.com/cloudwego/hertz/pkg/protocol/consts" 16 | ) 17 | 18 | // Upload . 19 | // @router /upload [POST] 20 | func Upload(ctx context.Context, c *app.RequestContext) { 21 | var _ image.Empty 22 | var req []byte 23 | _, err := service.NewUploadService(ctx, c).Run(&req) 24 | if err != nil { 25 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 26 | return 27 | } 28 | // 以上代码无用,因为每次更新idl都会引入service和model导致编译不通过 29 | file, err := c.FormFile("file") 30 | if err != nil { 31 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 32 | return 33 | } 34 | ext := filepath.Ext(file.Filename) 35 | // 检查是否为常见图片格式 36 | if ext != ".jpeg" && ext != ".png" && ext != ".jpg" { 37 | utils.SendErrResponse(ctx, c, consts.StatusOK, errors.New("不允许的类型")) 38 | return 39 | } 40 | uuid, err := utils.RandomUUID() 41 | fp := fmt.Sprintf("%s%s", uuid, ext) 42 | // 获取项目的根目录 43 | rootDir, err := os.Getwd() 44 | if err != nil { 45 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 46 | return 47 | } 48 | relativeDir := "upload/imgs/blogs" 49 | dir := filepath.Join(rootDir, relativeDir) 50 | err = utils.CreateDir(dir) 51 | if err != nil { 52 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 53 | return 54 | } 55 | err = c.SaveUploadedFile(file, filepath.Join(dir, fp)) 56 | if err != nil { 57 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 58 | return 59 | } 60 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, fmt.Sprintf("/blogs/%s", fp)) 61 | } 62 | 63 | // 64 | //// Upload . 65 | //// @router /upload [POST] 66 | //func Upload(ctx context.Context, c *app.RequestContext) { 67 | // var err error 68 | // var _ image.Empty 69 | // var req []byte 70 | // err = c.BindAndValidate(&req) 71 | // if err != nil { 72 | // utils.SendErrResponse(ctx, c, consts.StatusOK, err) 73 | // return 74 | // } 75 | // // 将req存为图片文件 76 | // _, format, err := image2.DecodeConfig(bytes.NewReader(req)) 77 | // if err != nil { 78 | // utils.SendErrResponse(ctx, c, consts.StatusOK, err) 79 | // } 80 | // // 检查是否为常见图片格式 81 | // if format != "jpeg" && format != "png" && format != "gif" { 82 | // utils.SendErrResponse(ctx, c, consts.StatusOK, errors.New("unsupported image format")) 83 | // } 84 | // //生成文件路径 /uplaod/uuid.ext 85 | // uuid, err := utils.RandomUUID() 86 | // filePath := fmt.Sprintf("/upload/img/blogs/%s.%s", uuid, format) 87 | // // 存储图片 88 | // file, err := os.Create(filePath) 89 | // if err != nil { 90 | // utils.SendErrResponse(ctx, c, consts.StatusOK, err) 91 | // } 92 | // defer file.Close() 93 | // _, err = io.Copy(file, bytes.NewReader(req)) 94 | // if err != nil { 95 | // utils.SendErrResponse(ctx, c, consts.StatusOK, err) 96 | // } 97 | // _, err = service.NewUploadService(ctx, c).Run(&req) 98 | // if err != nil { 99 | // utils.SendErrResponse(ctx, c, consts.StatusOK, err) 100 | // return 101 | // } 102 | // 103 | // utils.SendSuccessResponse(ctx, c, consts.StatusOK, fmt.Sprintf("/img/blogs/%s.%s", uuid, format)) 104 | //} 105 | -------------------------------------------------------------------------------- /biz/handler/image/image_service_test.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestUpload(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/upload/blog", Upload) 15 | w := ut.PerformRequest(h.Engine, "POST", "/upload/blog", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/handler/message/message_service.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | service "xzdp/biz/service/message" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/hlog" 11 | "github.com/hertz-contrib/sse" 12 | ) 13 | 14 | // Sse . 15 | // @router /sse [POST] 16 | func Sse(ctx context.Context, c *app.RequestContext) { 17 | s := sse.NewStream(c) 18 | //c.Status(consts.StatusOK) 19 | subCtx, cancel := context.WithCancel(ctx) 20 | defer cancel() 21 | for { 22 | select { 23 | case <-subCtx.Done(): 24 | hlog.Debugf("SSE stream closed") 25 | return 26 | default: 27 | req := ">" 28 | serv := service.NewSseService(subCtx, c) 29 | resp, err := serv.Run(req) 30 | if err != nil { 31 | hlog.Errorf("Error running SSE service: %v", err) 32 | continue 33 | } 34 | if resp == nil { 35 | continue 36 | } 37 | event := &sse.Event{ 38 | Event: "message", 39 | Data: []byte(*resp), 40 | } 41 | hlog.Debugf("SSE event: %v", event) 42 | err = s.Publish(event) 43 | if err := PublishWithRetry(s, event); err != nil { 44 | hlog.Errorf("Error publishing SSE event: %v", err) 45 | } 46 | } 47 | } 48 | } 49 | 50 | // PublishWithRetry 尝试发布事件,如果失败则重试 51 | func PublishWithRetry(s *sse.Stream, event *sse.Event) error { 52 | maxRetries := 3 // 最大重试次数 53 | retryDelay := time.Second // 重试间隔时间 54 | 55 | for i := 0; i <= maxRetries; i++ { 56 | err := s.Publish(event) 57 | if err == nil { 58 | return nil 59 | } 60 | 61 | if i < maxRetries { 62 | hlog.Errorf("Publish failed, retrying... (attempt %d/%d)", i+1, maxRetries) 63 | time.Sleep(retryDelay) 64 | } else { 65 | hlog.Errorf("Publish failed after %d attempts, giving up.", maxRetries) 66 | return err 67 | } 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /biz/handler/message/message_service_test.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestSse(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/message/sse", Sse) 15 | w := ut.PerformRequest(h.Engine, "GET", "/message/sse", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/handler/ping.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package handler 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/hertz/pkg/common/utils" 10 | "github.com/cloudwego/hertz/pkg/protocol/consts" 11 | ) 12 | 13 | // Ping . 14 | func Ping(ctx context.Context, c *app.RequestContext) { 15 | c.JSON(consts.StatusOK, utils.H{ 16 | "message": "pong", 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /biz/handler/shop/shop_service.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | shop "xzdp/biz/model/shop" 8 | service "xzdp/biz/service/shop" 9 | "xzdp/biz/utils" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | "github.com/cloudwego/hertz/pkg/protocol/consts" 13 | ) 14 | 15 | // ShopList . 16 | // @router /shop-type/list [GET] 17 | func ShopList(ctx context.Context, c *app.RequestContext) { 18 | var err error 19 | var req shop.Empty 20 | err = c.BindAndValidate(&req) 21 | if err != nil { 22 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 23 | return 24 | } 25 | 26 | resp, err := service.NewShopListService(ctx, c).Run(&req) 27 | if err != nil { 28 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 29 | return 30 | } 31 | 32 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 33 | } 34 | 35 | // ShopOfType . 36 | // @router /shop/of/type [GET] 37 | func ShopOfType(ctx context.Context, c *app.RequestContext) { 38 | var err error 39 | var req shop.ShopOfTypeReq 40 | err = c.BindAndValidate(&req) 41 | if err != nil { 42 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 43 | return 44 | } 45 | 46 | resp, err := service.NewShopOfTypeService(ctx, c).Run(&req) 47 | 48 | if err != nil { 49 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 50 | return 51 | } 52 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 53 | } 54 | 55 | // ShopInfo . 56 | // @router /shop/:id [GET] 57 | func ShopInfo(ctx context.Context, c *app.RequestContext) { 58 | var err error 59 | var req shop.Empty 60 | err = c.BindAndValidate(&req) 61 | if err != nil { 62 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 63 | return 64 | } 65 | id, err := strconv.ParseInt(c.Param("id"), 10, 64) 66 | if id <= 0 { 67 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 68 | return 69 | } 70 | 71 | resp, err := service.NewShopInfoService(ctx, c).Run(id) 72 | 73 | if err != nil { 74 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 75 | return 76 | } 77 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 78 | } 79 | 80 | // ShopOfTypeGeo . 81 | // @router /shop/of/type/geo [GET] 82 | func ShopOfTypeGeo(ctx context.Context, c *app.RequestContext) { 83 | var err error 84 | var req shop.ShopOfTypeGeoReq 85 | err = c.BindAndValidate(&req) 86 | if err != nil { 87 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 88 | return 89 | } 90 | 91 | resp, err := service.NewShopOfTypeGeoService(ctx, c).Run(&req) 92 | 93 | if err != nil { 94 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 95 | return 96 | } 97 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 98 | } 99 | -------------------------------------------------------------------------------- /biz/handler/shop/shop_service_test.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestShopList(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/shop-type/list", ShopList) 15 | w := ut.PerformRequest(h.Engine, "GET", "/shop-type/list", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/handler/user/user_service.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | model "xzdp/biz/model/user" 7 | service "xzdp/biz/service/user" 8 | "xzdp/biz/utils" 9 | 10 | "github.com/cloudwego/hertz/pkg/app" 11 | "github.com/cloudwego/hertz/pkg/protocol/consts" 12 | ) 13 | 14 | // SendCode . 15 | // @router /user/code [POST] 16 | func SendCode(ctx context.Context, c *app.RequestContext) { 17 | var err error 18 | var req model.UserLoginFrom 19 | err = c.BindAndValidate(&req) 20 | if err != nil { 21 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 22 | return 23 | } 24 | 25 | resp, err := service.NewSendCodeService(ctx, c).Run(&req) 26 | if err != nil { 27 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 28 | return 29 | } 30 | 31 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 32 | } 33 | 34 | // UserLogin . 35 | // @router /user/login [POST] 36 | func UserLogin(ctx context.Context, c *app.RequestContext) { 37 | var err error 38 | var req model.UserLoginFrom 39 | err = c.BindAndValidate(&req) 40 | if err != nil { 41 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 42 | return 43 | } 44 | resp, err := service.NewUserLoginService(ctx, c).Run(&req) 45 | if err != nil { 46 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 47 | return 48 | } 49 | 50 | utils.SendRawResponse(ctx, c, consts.StatusOK, resp) 51 | } 52 | 53 | // UserMe . 54 | // @router /user/me [GET] 55 | func UserMe(ctx context.Context, c *app.RequestContext) { 56 | var err error 57 | var req model.Empty 58 | err = c.BindAndValidate(&req) 59 | if err != nil { 60 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 61 | return 62 | } 63 | resp, err := service.NewUserMeService(ctx, c).Run(&req) 64 | 65 | if err != nil { 66 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 67 | return 68 | } 69 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 70 | } 71 | 72 | // UserInfo . 73 | // @router /user/info/:id [GET] 74 | func UserInfo(ctx context.Context, c *app.RequestContext) { 75 | var err error 76 | var req model.UserLoginFrom 77 | err = c.BindAndValidate(&req) 78 | if err != nil { 79 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 80 | return 81 | } 82 | 83 | resp, err := service.NewUserInfoService(ctx, c).Run(&req, c) 84 | 85 | if err != nil { 86 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 87 | return 88 | } 89 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 90 | } 91 | 92 | // UserSign . 93 | // @router /user/sign [GET] 94 | func UserSign(ctx context.Context, c *app.RequestContext) { 95 | var err error 96 | var req model.Empty 97 | err = c.BindAndValidate(&req) 98 | if err != nil { 99 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 100 | return 101 | } 102 | 103 | resp, err := service.NewUserSignService(ctx, c).Run(&req) 104 | 105 | if err != nil { 106 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 107 | return 108 | } 109 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 110 | } 111 | 112 | // UserSignCount . 113 | // @router /user/sign/count [GET] 114 | func UserSignCount(ctx context.Context, c *app.RequestContext) { 115 | var err error 116 | var req model.Empty 117 | err = c.BindAndValidate(&req) 118 | if err != nil { 119 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 120 | return 121 | } 122 | 123 | resp, err := service.NewUserSignCountService(ctx, c).Run(&req) 124 | 125 | if err != nil { 126 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 127 | return 128 | } 129 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 130 | } 131 | -------------------------------------------------------------------------------- /biz/handler/user/user_service_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestUserMethod(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/user/me", UserMe) 15 | w := ut.PerformRequest(h.Engine, "GET", "/user/me", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | 23 | func TestSendCode(t *testing.T) { 24 | h := server.Default() 25 | h.GET("/user/code", SendCode) 26 | w := ut.PerformRequest(h.Engine, "POST", "/user/code", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 27 | ut.Header{}) 28 | resp := w.Result() 29 | assert.DeepEqual(t, 201, resp.StatusCode()) 30 | assert.DeepEqual(t, "", string(resp.Body())) 31 | // todo edit your unit test. 32 | } 33 | 34 | func TestUserLogin(t *testing.T) { 35 | h := server.Default() 36 | h.GET("/user/login", UserLogin) 37 | w := ut.PerformRequest(h.Engine, "POST", "/user/login", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 38 | ut.Header{}) 39 | resp := w.Result() 40 | assert.DeepEqual(t, 201, resp.StatusCode()) 41 | assert.DeepEqual(t, "", string(resp.Body())) 42 | // todo edit your unit test. 43 | } 44 | 45 | func TestUserInfo(t *testing.T) { 46 | h := server.Default() 47 | h.GET("/user/:id", UserInfo) 48 | w := ut.PerformRequest(h.Engine, "GET", "/user/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 49 | ut.Header{}) 50 | resp := w.Result() 51 | assert.DeepEqual(t, 201, resp.StatusCode()) 52 | assert.DeepEqual(t, "", string(resp.Body())) 53 | // todo edit your unit test. 54 | } 55 | -------------------------------------------------------------------------------- /biz/handler/voucher/voucher_service.go: -------------------------------------------------------------------------------- 1 | package voucher 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/hertz/pkg/protocol/consts" 10 | voucher "xzdp/biz/model/voucher" 11 | service "xzdp/biz/service/voucher" 12 | "xzdp/biz/utils" 13 | ) 14 | 15 | // VoucherList . 16 | // @router /voucher/list/:id [GET] 17 | func VoucherList(ctx context.Context, c *app.RequestContext) { 18 | var err error 19 | var req voucher.Empty 20 | err = c.BindAndValidate(&req) 21 | if err != nil { 22 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 23 | return 24 | } 25 | 26 | id, err := strconv.ParseInt(c.Param("id"), 10, 64) 27 | if id <= 0 { 28 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 29 | return 30 | } 31 | resp, err := service.NewVoucherListService(ctx, c).Run(id) 32 | 33 | if err != nil { 34 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 35 | return 36 | } 37 | 38 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 39 | } 40 | 41 | // SeckillVoucher . 42 | // @router /voucher-order/seckill/:id [POST] 43 | func SeckillVoucher(ctx context.Context, c *app.RequestContext) { 44 | var err error 45 | voucherString := c.Param("id") 46 | if voucherString == "" { 47 | utils.SendErrResponse(ctx, c, consts.StatusOK, errors.New("参数错误")) 48 | return 49 | } 50 | voucherID, err := strconv.ParseInt(voucherString, 10, 64) 51 | if err != nil { 52 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 53 | return 54 | } 55 | resp, err := service.NewSeckillVoucherService(ctx, c).Run(&voucherID) 56 | if err != nil { 57 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 58 | return 59 | } 60 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 61 | } 62 | -------------------------------------------------------------------------------- /biz/handler/voucher/voucher_service_test.go: -------------------------------------------------------------------------------- 1 | package voucher 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestVoucherList(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/voucher/list/:id", VoucherList) 15 | w := ut.PerformRequest(h.Engine, "GET", "/voucher/list/:id", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/handler/xzdp/hello_service.go: -------------------------------------------------------------------------------- 1 | package xzdp 2 | 3 | import ( 4 | "context" 5 | 6 | xzdp "xzdp/biz/model/xzdp" 7 | service "xzdp/biz/service/xzdp" 8 | "xzdp/biz/utils" 9 | 10 | "github.com/cloudwego/hertz/pkg/app" 11 | "github.com/cloudwego/hertz/pkg/protocol/consts" 12 | ) 13 | 14 | // HelloMethod . 15 | // @router /hello [GET] 16 | func HelloMethod(ctx context.Context, c *app.RequestContext) { 17 | var err error 18 | var req xzdp.HelloReq 19 | err = c.BindAndValidate(&req) 20 | if err != nil { 21 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 22 | return 23 | } 24 | 25 | resp, err := service.NewHelloMethodService(ctx, c).Run(&req) 26 | if err != nil { 27 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 28 | return 29 | } 30 | 31 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 32 | } 33 | -------------------------------------------------------------------------------- /biz/handler/xzdp/hello_service_test.go: -------------------------------------------------------------------------------- 1 | package xzdp 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | "github.com/cloudwego/hertz/pkg/common/ut" 10 | ) 11 | 12 | func TestHelloMethod(t *testing.T) { 13 | h := server.Default() 14 | h.GET("/hello", HelloMethod) 15 | w := ut.PerformRequest(h.Engine, "GET", "/hello", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 16 | ut.Header{}) 17 | resp := w.Result() 18 | assert.DeepEqual(t, 201, resp.StatusCode()) 19 | assert.DeepEqual(t, "", string(resp.Body())) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/middleware/interceptor/cors.go: -------------------------------------------------------------------------------- 1 | package interceptor 2 | 3 | import ( 4 | "context" 5 | "github.com/cloudwego/hertz/pkg/app" 6 | "github.com/cloudwego/hertz/pkg/common/hlog" 7 | "net/http" 8 | "xzdp/conf" 9 | ) 10 | 11 | // AllowAllCors 直接放行所有跨域请求并放行所有 OPTIONS 方法 12 | func allowAllCors(ctx context.Context, c *app.RequestContext) { 13 | method := c.Request.Method() 14 | origin := c.Request.Header.Get("Origin") 15 | c.Header("Access-Control-Allow-Origin", origin) 16 | c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id") 17 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT") 18 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") 19 | c.Header("Access-Control-Allow-Credentials", "true") 20 | 21 | if string(method) == "OPTIONS" { 22 | c.AbortWithStatus(http.StatusNoContent) 23 | return 24 | } 25 | c.Next(ctx) 26 | hlog.Debugf("AllowCors:Method:%+v,Path:%+v", string(c.Request.Method()), string(c.Request.Path())) 27 | } 28 | 29 | // Cors 按照配置处理跨域请求 30 | func Cors(ctx context.Context, c *app.RequestContext) { 31 | mode := conf.GetConf().Cors.Mode 32 | // 放行全部 33 | if mode == "allow-all" { 34 | allowAllCors(ctx, c) 35 | return 36 | } 37 | whitelist := checkCors(string(c.GetHeader("origin"))) 38 | // 通过检查, 添加请求头 39 | if whitelist != nil { 40 | c.Header("Access-Control-Allow-Origin", whitelist.AllowOrigin) 41 | c.Header("Access-Control-Allow-Headers", whitelist.AllowHeaders) 42 | c.Header("Access-Control-Allow-Methods", whitelist.AllowMethods) 43 | c.Header("Access-Control-Expose-Headers", whitelist.ExposeHeaders) 44 | if whitelist.AllowCredentials { 45 | c.Header("Access-Control-Allow-Credentials", "true") 46 | } 47 | } 48 | 49 | // 严格白名单模式且未通过检查,直接拒绝处理请求 50 | if whitelist == nil && mode == "strict-whitelist" && !(string(c.Request.Method()) == "GET") { 51 | c.AbortWithStatus(http.StatusForbidden) 52 | return 53 | } else { 54 | // 非严格白名单模式,无论是否通过检查均放行除了所有 OPTIONS 方法 55 | if string(c.Request.Method()) == "OPTIONS" { 56 | c.AbortWithStatus(http.StatusNoContent) 57 | return 58 | } 59 | } 60 | 61 | // 处理请求 62 | c.Next(ctx) 63 | 64 | } 65 | 66 | func checkCors(currentOrigin string) *conf.CORSWhitelist { 67 | for _, whitelist := range conf.GetConf().Cors.WhiteList { 68 | // 遍历配置中的跨域头,寻找匹配项 69 | if currentOrigin == whitelist.AllowOrigin { 70 | return &whitelist 71 | } 72 | } 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /biz/middleware/interceptor/hll.go: -------------------------------------------------------------------------------- 1 | package interceptor 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | "xzdp/biz/dal/redis" 8 | "xzdp/biz/pkg/constants" 9 | "xzdp/biz/utils" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | "github.com/cloudwego/hertz/pkg/common/hlog" 13 | ) 14 | 15 | func UniqueVisitor(ctx context.Context, c *app.RequestContext) { 16 | hlog.CtxInfof(ctx, "Unique Visitor") 17 | userDTO := utils.GetUser(ctx) 18 | if userDTO == nil { 19 | c.Next(ctx) 20 | return 21 | } 22 | hlog.CtxInfof(ctx, "Unique Visitor userDTO: %v", userDTO) 23 | now := time.Now() 24 | 25 | // 格式化为 YYYY-MM-DD 格式 26 | today := now.Format("2006-01-02") 27 | hhlVal := fmt.Sprint(userDTO.ID) + today 28 | err := redis.RedisClient.PFAdd(ctx, constants.HLL_UV_KEY, hhlVal) 29 | if err != nil { 30 | hlog.CtxErrorf(ctx, "PFAdd error: %v", err) 31 | } 32 | c.Next(ctx) 33 | } 34 | -------------------------------------------------------------------------------- /biz/middleware/interceptor/refresh_token.go: -------------------------------------------------------------------------------- 1 | package interceptor 2 | 3 | import ( 4 | "context" 5 | "github.com/cloudwego/hertz/pkg/app" 6 | "github.com/cloudwego/hertz/pkg/common/hlog" 7 | "time" 8 | "xzdp/biz/dal/redis" 9 | model "xzdp/biz/model/user" 10 | "xzdp/biz/pkg/constants" 11 | "xzdp/biz/utils" 12 | "xzdp/conf" 13 | ) 14 | 15 | func CheckToken(ctx context.Context, c *app.RequestContext) { 16 | hlog.CtxInfof(ctx, "check token interceptor:%+v", conf.GetEnv()) 17 | //if conf.GetEnv() != "online" { 18 | // userdto := model.UserDTO{ 19 | // ID: 2, 20 | // NickName: "法外狂徒张三", 21 | // Icon: "https://img2.baidu.com/it/u=194756667,2850459164&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500", 22 | // } 23 | // ctx = utils.SaveUser(ctx, &userdto) 24 | // c.Next(ctx) 25 | // return 26 | //} 27 | token := c.GetHeader("authorization") 28 | if token == nil { 29 | c.Next(ctx) 30 | } 31 | hlog.CtxInfof(ctx, "token = %s", token) 32 | if len(token) == 0 { 33 | c.Next(ctx) 34 | } 35 | var userdto model.UserDTO 36 | if err := redis.RedisClient.HGetAll(ctx, constants.LOGIN_USER_KEY+string(token)).Scan(&userdto); err != nil { 37 | c.Next(ctx) 38 | } 39 | if userdto == (model.UserDTO{}) { 40 | c.Next(ctx) 41 | } 42 | redis.RedisClient.Expire(ctx, constants.LOGIN_USER_KEY+string(token), time.Minute*1) 43 | ctx = utils.SaveUser(ctx, &userdto) 44 | c.Next(ctx) 45 | if utils.GetUser(ctx) == nil { 46 | hlog.CtxErrorf(ctx, "check token interceptor error") 47 | } 48 | hlog.CtxDebugf(ctx, "user = %+v", utils.GetUser(ctx)) 49 | } 50 | 51 | func LoginInterceptor(ctx context.Context, c *app.RequestContext) { 52 | hlog.CtxInfof(ctx, "login interceptor") 53 | if conf.GetEnv() == "dev" { 54 | c.Next(ctx) 55 | return 56 | } 57 | if utils.GetUser(ctx) == nil { 58 | c.SetStatusCode(401) 59 | c.Abort() 60 | } 61 | c.Next(ctx) 62 | } 63 | -------------------------------------------------------------------------------- /biz/model/blog/table.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | func (Blog) TableName() string { 4 | return "tb_blog" 5 | } 6 | -------------------------------------------------------------------------------- /biz/model/cache/redis_data.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "time" 4 | 5 | type RedisStringData struct { 6 | Data string 7 | ExpiredTime time.Time 8 | } 9 | 10 | func NewRedisStringData(data string, expiredTime time.Time) *RedisStringData { 11 | return &RedisStringData{data, expiredTime} 12 | } 13 | -------------------------------------------------------------------------------- /biz/model/shop/shop_mapper.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | func (Shop) TableName() string { 4 | return "tb_shop" 5 | } 6 | -------------------------------------------------------------------------------- /biz/model/user/mapper.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "time" 5 | 6 | "gorm.io/gorm" 7 | ) 8 | 9 | func (User) TableName() string { 10 | return "tb_user" 11 | } 12 | 13 | func (UserInfo) TableName() string { 14 | return "tb_user_info" 15 | } 16 | 17 | // BeforeCreate 是一个 GORM 钩子函数,在插入数据前执行 18 | func (u *User) BeforeCreate(tx *gorm.DB) (err error) { 19 | u.CreateTime = time.Now().Format("2006-01-02 15:04:05") 20 | u.UpdateTime = time.Now().Format("2006-01-02 15:04:05") 21 | return 22 | } 23 | 24 | func (u *UserInfo) BeforeCreate(tx *gorm.DB) (err error) { 25 | u.Fans = 0 26 | u.Birthday = time.Now().Format("2006-01-02 15:04:05") 27 | u.Followee = 0 28 | u.Credits = 0 29 | u.Gender = 1 30 | u.Introduce = "" 31 | u.Level = 0 32 | u.City = "北京" 33 | u.CreateTime = time.Now().Format("2006-01-02 15:04:05") 34 | u.UpdateTime = time.Now().Format("2006-01-02 15:04:05") 35 | return 36 | } 37 | 38 | // func (u UserDTO) MarshalBinary() ([]byte, error) { 39 | // return json.Marshal(u) 40 | // } 41 | -------------------------------------------------------------------------------- /biz/model/xzdp/response.go: -------------------------------------------------------------------------------- 1 | package xzdp 2 | 3 | type Response struct { 4 | Success bool `json:"success"` 5 | Data interface{} `json:"data"` 6 | } 7 | type FailResponse struct { 8 | Success bool `json:"success"` 9 | ErrmMsg string `json:"errorMsg"` 10 | } 11 | 12 | func NewSuccessResponse(data interface{}) *Response { 13 | return &Response{ 14 | Success: true, 15 | Data: data, 16 | } 17 | } 18 | 19 | func NewFailureResponse(message string) *FailResponse { 20 | return &FailResponse{ 21 | Success: false, 22 | ErrmMsg: message, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /biz/pkg/cache/user.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/cloudwego/hertz/pkg/common/hlog" 9 | redis2 "github.com/go-redis/redis/v8" 10 | "xzdp/biz/dal/mysql" 11 | "xzdp/biz/dal/redis" 12 | "xzdp/biz/model/user" 13 | "xzdp/biz/pkg/constants" 14 | "xzdp/biz/utils" 15 | ) 16 | 17 | func GetUserDtoFromCacheOrDB(ctx context.Context, userID int64) (*user.UserDTO, error) { 18 | key := fmt.Sprintf("%s%d", constants.CACHE_USERDTO_KEY, userID) 19 | // Try to get the user from cache. 20 | cachedUser, err := redis.RedisClient.Get(ctx, key).Result() 21 | if err != nil && !errors.Is(err, redis2.Nil) { 22 | return nil, err 23 | } 24 | if err == nil { 25 | // If we have a value, unmarshal it into a user struct. 26 | var interUser user.User 27 | if err := json.Unmarshal([]byte(cachedUser), &interUser); err != nil { 28 | return nil, err 29 | } 30 | dto := utils.UserToUserDTO(&interUser) 31 | return dto, nil 32 | } 33 | 34 | // If not in cache, fetch from DB. 35 | interUser, err := mysql.GetById(ctx, userID) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | // Save the user to cache. 41 | userJSON, err := json.Marshal(interUser) 42 | if err != nil { 43 | return nil, err 44 | } 45 | if err := redis.RedisClient.Set(ctx, key, userJSON, constants.CACHE_USERDTO_EXPIRE).Err(); err != nil { 46 | hlog.CtxWarnf(ctx, "Failed to set user cache: %v", err) 47 | } 48 | dto := utils.UserToUserDTO(interUser) 49 | return dto, nil 50 | } 51 | -------------------------------------------------------------------------------- /biz/pkg/constants/constants.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import "time" 4 | 5 | const ( 6 | CACHE_SHOP_TYPE_LIST_KEY = "cache:shopType" 7 | MAX_PAGE_SIZE = 10 8 | LOGIN_CODE_KEY = "login:code:" 9 | LOGIN_USER_KEY = "login:token:" 10 | DEFAULT_PAGE_SIZE = 5 11 | CACHE_SHOP_KEY = "cache:shop:" 12 | CACHE_NULL_TTL = time.Minute * 2 13 | CACHE_SHOP_TTL = time.Minute * 30 14 | SHOP_GEO_KEY = "shop:geo:" 15 | //CACHE_SHOP_TTL = 10 * time.Second 16 | LOCK_SHOP_KEY = "lock:shop:" 17 | LOCK_KEY = "lock:" 18 | LOGIN_CODE_EXPIRE = 300 19 | USER_SIGN_KEY = "user:sign:" 20 | HLL_UV_KEY = "HyperLogLog:uv" 21 | ) 22 | 23 | // cache 相关 24 | const ( 25 | CACHE_USERDTO_KEY = "userdto:" 26 | CACHE_USERDTO_EXPIRE = time.Minute * 5 27 | ) 28 | 29 | // follow 相关 30 | const ( 31 | FOLLOW_USER_KEY = "follow:user:" 32 | ) 33 | 34 | // blog 相关 35 | const ( 36 | BLOG_LIKED_KEY = "blog:liked:" 37 | FEED_KEY = "feed:" 38 | ) 39 | 40 | // message 相关 41 | const ( 42 | MESSAGE_STREAM_KEY = "message.stream:" 43 | STREAM_READ_GROUP = "stream.group:1" 44 | STREAM_CONSUMER = "stream.consume:" 45 | ) 46 | 47 | // voucher 相关 48 | const ( 49 | BEGIN_TIMESTAMP = 1725120000 50 | ICRID_KEY = "id:" 51 | COUNT_BIT = 32 52 | VOUCHER_LOCK_KEY = "voucher:lock:" 53 | ) 54 | -------------------------------------------------------------------------------- /biz/router/blog/blog.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package blog 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | blog "xzdp/biz/handler/blog" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | root.POST("/blog", append(_postblogMw(), blog.PostBlog)...) 21 | _blog := root.Group("/blog", _blogMw()...) 22 | _blog.DELETE("/:id", append(_deleteblogMw(), blog.DeleteBlog)...) 23 | _blog.GET("/:id", append(_getblogMw(), blog.GetBlog)...) 24 | { 25 | _like := _blog.Group("/like", _likeMw()...) 26 | _like.PUT("/:id", append(_likeblogMw(), blog.LikeBlog)...) 27 | } 28 | { 29 | _likes := _blog.Group("/likes", _likesMw()...) 30 | _likes.GET("/:id", append(_getlikesMw(), blog.GetLikes)...) 31 | } 32 | { 33 | _of := _blog.Group("/of", _ofMw()...) 34 | _of.GET("/follow", append(_getfollowblogMw(), blog.GetFollowBlog)...) 35 | } 36 | { 37 | _blog0 := root.Group("/blog", _blog0Mw()...) 38 | _blog0.GET("/hot", append(_gethotblogMw(), blog.GetHotBlog)...) 39 | { 40 | _of0 := _blog0.Group("/of", _of0Mw()...) 41 | _of0.GET("/me", append(_blogofmeMw(), blog.BlogOfMe)...) 42 | } 43 | { 44 | _user := _blog0.Group("/user", _userMw()...) 45 | _user.GET("/:id", append(_getuserblogMw(), blog.GetUserBlog)...) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /biz/router/blog/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package blog 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _blogMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _gethotblogMw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _ofMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _getblogofmeMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _getblogMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _postblogMw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | 44 | func _likeMw() []app.HandlerFunc { 45 | // your code... 46 | return nil 47 | } 48 | 49 | func _likeblogMw() []app.HandlerFunc { 50 | // your code... 51 | return nil 52 | } 53 | 54 | func _likesMw() []app.HandlerFunc { 55 | // your code... 56 | return nil 57 | } 58 | 59 | func _getlikesMw() []app.HandlerFunc { 60 | // your code... 61 | return nil 62 | } 63 | 64 | func _deleteblogMw() []app.HandlerFunc { 65 | // your code... 66 | return nil 67 | } 68 | 69 | func _userMw() []app.HandlerFunc { 70 | // your code... 71 | return nil 72 | } 73 | 74 | func _getuserblogMw() []app.HandlerFunc { 75 | // your code... 76 | return nil 77 | } 78 | 79 | func _getfollowblogMw() []app.HandlerFunc { 80 | // your code... 81 | return nil 82 | } 83 | 84 | func _blog0Mw() []app.HandlerFunc { 85 | // your code... 86 | return nil 87 | } 88 | 89 | func _of0Mw() []app.HandlerFunc { 90 | // your code... 91 | return nil 92 | } 93 | 94 | func _blogofmeMw() []app.HandlerFunc { 95 | // your code... 96 | return nil 97 | } 98 | -------------------------------------------------------------------------------- /biz/router/blog_comment/blog_comment.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package blog_comment 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | blog_comment "xzdp/biz/handler/blog_comment" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _comment := root.Group("/comment", _commentMw()...) 22 | _comment.GET("/:blogID", append(_getcommentMw(), blog_comment.GetComment)...) 23 | _comment.GET("/hot", append(_gethotcommentMw(), blog_comment.GetHotComment)...) 24 | _comment.DELETE("/:id", append(_deletecommentMw(), blog_comment.DeleteComment)...) 25 | _comment.POST("/post", append(_postcommentMw(), blog_comment.PostComment)...) 26 | { 27 | _like := _comment.Group("/like", _likeMw()...) 28 | _like.PUT("/:id", append(_likecommentMw(), blog_comment.LikeComment)...) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /biz/router/blog_comment/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package blog_comment 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _commentMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _getcommentMw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _gethotcommentMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _deletecommentMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _postcommentMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _likeMw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | 44 | func _likecommentMw() []app.HandlerFunc { 45 | // your code... 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /biz/router/follow/follow.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package follow 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | follow "xzdp/biz/handler/follow" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _follow := root.Group("/follow", _followMw()...) 22 | { 23 | _common := _follow.Group("/common", _commonMw()...) 24 | _common.GET("/:id", append(_commonfollowMw(), follow.CommonFollow)...) 25 | } 26 | { 27 | _id := _follow.Group("/:id", _idMw()...) 28 | _id.PUT("/:isFollow", append(_follow0Mw(), follow.Follow)...) 29 | } 30 | { 31 | _or := _follow.Group("/or", _orMw()...) 32 | { 33 | _not := _or.Group("/not", _notMw()...) 34 | _not.GET("/:id", append(_isfollowedMw(), follow.IsFollowed)...) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /biz/router/follow/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package follow 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _followMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _follow0Mw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _isfollowedMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _commonMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _commonfollowMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _isfollowed0Mw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | 44 | func _idMw() []app.HandlerFunc { 45 | // your code... 46 | return nil 47 | } 48 | 49 | func _orMw() []app.HandlerFunc { 50 | // your code... 51 | return nil 52 | } 53 | 54 | func _notMw() []app.HandlerFunc { 55 | // your code... 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /biz/router/image/image.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package image 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | image "xzdp/biz/handler/image" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _upload := root.Group("/upload", _uploadMw()...) 22 | _upload.POST("/blog", append(_upload0Mw(), image.Upload)...) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /biz/router/image/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package image 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _uploadMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _upload0Mw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /biz/router/message/message.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package message 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | message "xzdp/biz/handler/message" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _message := root.Group("/message", _messageMw()...) 22 | _message.GET("/sse", append(_sseMw(), message.Sse)...) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /biz/router/message/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package message 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _sseMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _e6Mw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _messageMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /biz/router/register.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package router 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | blog "xzdp/biz/router/blog" 8 | blog_comment "xzdp/biz/router/blog_comment" 9 | follow "xzdp/biz/router/follow" 10 | image "xzdp/biz/router/image" 11 | message "xzdp/biz/router/message" 12 | shop "xzdp/biz/router/shop" 13 | user "xzdp/biz/router/user" 14 | voucher "xzdp/biz/router/voucher" 15 | xzdp "xzdp/biz/router/xzdp" 16 | ) 17 | 18 | // GeneratedRegister registers routers generated by IDL. 19 | func GeneratedRegister(r *server.Hertz) { 20 | //INSERT_POINT: DO NOT DELETE THIS LINE! 21 | voucher.Register(r) 22 | 23 | message.Register(r) 24 | 25 | blog_comment.Register(r) 26 | 27 | image.Register(r) 28 | 29 | follow.Register(r) 30 | 31 | shop.Register(r) 32 | 33 | user.Register(r) 34 | 35 | blog.Register(r) 36 | 37 | xzdp.Register(r) 38 | } 39 | -------------------------------------------------------------------------------- /biz/router/shop/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package shop 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _shop_typeMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _shoplistMw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _shopMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _ofMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _shopoftypeMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _shopinfoMw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | 44 | func _typeMw() []app.HandlerFunc { 45 | // your code... 46 | return nil 47 | } 48 | 49 | func _shopoftypegeoMw() []app.HandlerFunc { 50 | // your code... 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /biz/router/shop/shop.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package shop 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | shop "xzdp/biz/handler/shop" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _shop := root.Group("/shop", _shopMw()...) 22 | _shop.GET("/:id", append(_shopinfoMw(), shop.ShopInfo)...) 23 | { 24 | _of := _shop.Group("/of", _ofMw()...) 25 | _of.GET("/type", append(_shopoftypeMw(), shop.ShopOfType)...) 26 | _type := _of.Group("/type", _typeMw()...) 27 | _type.GET("/geo", append(_shopoftypegeoMw(), shop.ShopOfTypeGeo)...) 28 | } 29 | } 30 | { 31 | _shop_type := root.Group("/shop-type", _shop_typeMw()...) 32 | _shop_type.GET("/list", append(_shoplistMw(), shop.ShopList)...) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /biz/router/user/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package user 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _userMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _sendcodeMw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _userinfoMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _userloginMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _usermethodMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _usermeMw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | 44 | func _infoMw() []app.HandlerFunc { 45 | // your code... 46 | return nil 47 | } 48 | 49 | func _signMw() []app.HandlerFunc { 50 | // your code... 51 | return nil 52 | } 53 | 54 | func _usersignMw() []app.HandlerFunc { 55 | // your code... 56 | return nil 57 | } 58 | 59 | func _usersigncountMw() []app.HandlerFunc { 60 | // your code... 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /biz/router/user/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package user 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | user "xzdp/biz/handler/user" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _user := root.Group("/user", _userMw()...) 22 | _user.POST("/code", append(_sendcodeMw(), user.SendCode)...) 23 | _user.POST("/login", append(_userloginMw(), user.UserLogin)...) 24 | _user.GET("/me", append(_usermeMw(), user.UserMe)...) 25 | _user.POST("/sign", append(_usersignMw(), user.UserSign)...) 26 | _sign := _user.Group("/sign", _signMw()...) 27 | _sign.GET("/count", append(_usersigncountMw(), user.UserSignCount)...) 28 | { 29 | _info := _user.Group("/info", _infoMw()...) 30 | _info.GET("/:id", append(_userinfoMw(), user.UserInfo)...) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /biz/router/voucher/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package voucher 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _voucherMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _listMw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _voucherlistMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _voucher_orderMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _seckillMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _seckillvoucherMw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /biz/router/voucher/voucher.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package voucher 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | voucher "xzdp/biz/handler/voucher" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _voucher := root.Group("/voucher", _voucherMw()...) 22 | { 23 | _list := _voucher.Group("/list", _listMw()...) 24 | _list.GET("/:id", append(_voucherlistMw(), voucher.VoucherList)...) 25 | } 26 | } 27 | { 28 | _voucher_order := root.Group("/voucher-order", _voucher_orderMw()...) 29 | { 30 | _seckill := _voucher_order.Group("/seckill", _seckillMw()...) 31 | _seckill.POST("/:id", append(_seckillvoucherMw(), voucher.SeckillVoucher)...) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /biz/router/xzdp/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package xzdp 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | ) 8 | 9 | func rootMw() []app.HandlerFunc { 10 | // your code... 11 | return nil 12 | } 13 | 14 | func _hellomethodMw() []app.HandlerFunc { 15 | // your code... 16 | return nil 17 | } 18 | 19 | func _usermethodMw() []app.HandlerFunc { 20 | // your code... 21 | return nil 22 | } 23 | 24 | func _userMw() []app.HandlerFunc { 25 | // your code... 26 | return nil 27 | } 28 | 29 | func _userinfoMw() []app.HandlerFunc { 30 | // your code... 31 | return nil 32 | } 33 | 34 | func _userloginMw() []app.HandlerFunc { 35 | // your code... 36 | return nil 37 | } 38 | 39 | func _usercodeMw() []app.HandlerFunc { 40 | // your code... 41 | return nil 42 | } 43 | 44 | func _shop_typeMw() []app.HandlerFunc { 45 | // your code... 46 | return nil 47 | } 48 | 49 | func _shoplistMw() []app.HandlerFunc { 50 | // your code... 51 | return nil 52 | } 53 | 54 | func _blogMw() []app.HandlerFunc { 55 | // your code... 56 | return nil 57 | } 58 | 59 | func _gethotblogMw() []app.HandlerFunc { 60 | // your code... 61 | return nil 62 | } 63 | 64 | func _sendcodeMw() []app.HandlerFunc { 65 | // your code... 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /biz/router/xzdp/xzdp.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package xzdp 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | xzdp "xzdp/biz/handler/xzdp" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | root.GET("/hello", append(_hellomethodMw(), xzdp.HelloMethod)...) 21 | } 22 | -------------------------------------------------------------------------------- /biz/service/blog/blog_of_me.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "xzdp/biz/dal/mysql" 6 | "xzdp/biz/model/user" 7 | "xzdp/biz/utils" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | blog "xzdp/biz/model/blog" 11 | ) 12 | 13 | type BlogOfMeService struct { 14 | RequestContext *app.RequestContext 15 | Context context.Context 16 | } 17 | 18 | func NewBlogOfMeService(Context context.Context, RequestContext *app.RequestContext) *BlogOfMeService { 19 | return &BlogOfMeService{RequestContext: RequestContext, Context: Context} 20 | } 21 | 22 | func (h *BlogOfMeService) Run(req *blog.BlogReq) (resp *[]*blog.Blog, err error) { 23 | //defer func() { 24 | // hlog.CtxInfof(h.Context, "req = %+v", req) 25 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 26 | //}() 27 | // todo edit your code 28 | u := utils.GetUser(h.Context) 29 | d := &user.UserDTO{ 30 | ID: u.ID, 31 | NickName: u.NickName, 32 | Icon: u.Icon, 33 | } 34 | blogList, err := mysql.QueryBlogByUserID(h.Context, int(req.Current), d) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return &blogList, nil 39 | 40 | } 41 | -------------------------------------------------------------------------------- /biz/service/blog/blog_of_me_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog "xzdp/biz/model/blog" 10 | ) 11 | 12 | func TestBlogOfMeService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewBlogOfMeService(ctx, c) 16 | // init req and assert value 17 | req := &blog.BlogReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog/delete_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | "xzdp/biz/dal/mysql" 8 | "xzdp/biz/dal/redis" 9 | "xzdp/biz/pkg/constants" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | blog "xzdp/biz/model/blog" 13 | ) 14 | 15 | type DeleteBlogService struct { 16 | RequestContext *app.RequestContext 17 | Context context.Context 18 | } 19 | 20 | func NewDeleteBlogService(Context context.Context, RequestContext *app.RequestContext) *DeleteBlogService { 21 | return &DeleteBlogService{RequestContext: RequestContext, Context: Context} 22 | } 23 | 24 | func (h *DeleteBlogService) Run(req *string) (resp *blog.Empty, err error) { 25 | //defer func() { 26 | // hlog.CtxInfof(h.Context, "req = %+v", req) 27 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 28 | //}() 29 | // todo edit your code 30 | key := constants.BLOG_LIKED_KEY + *req 31 | // 从redis删除点赞数据 32 | ok, err := redis.HasLikes(h.Context, key) 33 | if err != nil { 34 | return nil, err 35 | } 36 | if ok { 37 | if !errors.Is(redis.DeleteLikes(h.Context, key), nil) { 38 | return nil, err 39 | } 40 | } 41 | // 删除评论信息 42 | bid, err := strconv.ParseInt(*req, 10, 64) 43 | if err != nil { 44 | return nil, err 45 | } 46 | err = mysql.DeleteBlogComment(h.Context, bid) 47 | if !errors.Is(err, nil) { 48 | return nil, err 49 | } 50 | // 从数据库删除博客 51 | err = mysql.DB.Where("id = ?", req).Delete(&blog.Blog{}).Error 52 | if !errors.Is(err, nil) { 53 | return nil, err 54 | } 55 | return &blog.Empty{}, nil 56 | } 57 | -------------------------------------------------------------------------------- /biz/service/blog/delete_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | ) 10 | 11 | func TestDeleteBlogService_Run(t *testing.T) { 12 | ctx := context.Background() 13 | c := app.NewContext(1) 14 | s := NewDeleteBlogService(ctx, c) 15 | // init req and assert value 16 | req := &string{} 17 | resp, err := s.Run(req) 18 | assert.DeepEqual(t, nil, resp) 19 | assert.DeepEqual(t, nil, err) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/service/blog/get_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | "xzdp/biz/dal/mysql" 8 | "xzdp/biz/dal/redis" 9 | "xzdp/biz/pkg/constants" 10 | "xzdp/biz/utils" 11 | 12 | "github.com/cloudwego/hertz/pkg/app" 13 | blog "xzdp/biz/model/blog" 14 | ) 15 | 16 | type GetBlogService struct { 17 | RequestContext *app.RequestContext 18 | Context context.Context 19 | } 20 | 21 | func NewGetBlogService(Context context.Context, RequestContext *app.RequestContext) *GetBlogService { 22 | return &GetBlogService{RequestContext: RequestContext, Context: Context} 23 | } 24 | 25 | func (h *GetBlogService) Run(req *string) (resp *blog.Blog, err error) { 26 | //defer func() { 27 | // hlog.CtxInfof(h.Context, "req = %+v", req) 28 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 29 | //}() 30 | // todo edit your code 31 | if !errors.Is(mysql.DB.First(&resp, "id = ?", req).Error, nil) { 32 | return nil, errors.New("未找到该博客") 33 | } 34 | userId := resp.UserId 35 | user, err := mysql.GetById(h.Context, userId) 36 | if err != nil { 37 | return nil, err 38 | } 39 | resp.Icon = user.Icon 40 | resp.NickName = user.NickName 41 | resp.IsLiked = false 42 | // 获取点赞状态 43 | u := utils.GetUser(h.Context).GetID() 44 | key := constants.BLOG_LIKED_KEY + *req 45 | isLike, err := redis.IsLiked(h.Context, key, strconv.FormatInt(u, 10)) 46 | if err != nil { 47 | return nil, err 48 | } 49 | resp.IsLiked = isLike 50 | return resp, nil 51 | } 52 | -------------------------------------------------------------------------------- /biz/service/blog/get_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog "xzdp/biz/model/blog" 10 | ) 11 | 12 | func TestGetBlogService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewGetBlogService(ctx, c) 16 | // init req and assert value 17 | req := &string{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog/get_follow_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "gorm.io/gorm" 7 | "strconv" 8 | "xzdp/biz/dal/mysql" 9 | "xzdp/biz/dal/redis" 10 | "xzdp/biz/pkg/constants" 11 | "xzdp/biz/utils" 12 | 13 | "github.com/cloudwego/hertz/pkg/app" 14 | blog "xzdp/biz/model/blog" 15 | ) 16 | 17 | type GetFollowBlogService struct { 18 | RequestContext *app.RequestContext 19 | Context context.Context 20 | } 21 | 22 | func NewGetFollowBlogService(Context context.Context, RequestContext *app.RequestContext) *GetFollowBlogService { 23 | return &GetFollowBlogService{RequestContext: RequestContext, Context: Context} 24 | } 25 | 26 | func (h *GetFollowBlogService) Run(req *blog.FollowBlogReq) (resp *blog.FollowBlogRresp, err error) { 27 | //defer func() { 28 | // hlog.CtxInfof(h.Context, "req = %+v", req) 29 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 30 | //}() 31 | // todo edit your code 32 | u := utils.GetUser(h.Context).GetID() 33 | key := constants.FEED_KEY + strconv.FormatInt(u, 10) 34 | zSet, err := redis.GetBlogsByKey(h.Context, key, req.LastId, req.Offset) 35 | var bids []string 36 | for _, z := range zSet { 37 | bids = append(bids, z.Member.(string)) 38 | } 39 | var blogs []*blog.Blog 40 | err = mysql.DB.Where("id in ?", bids).Find(&blogs).Error 41 | if errors.Is(err, gorm.ErrRecordNotFound) { 42 | return nil, errors.New("没有更多数据") 43 | } 44 | if err != nil { 45 | return nil, err 46 | } 47 | //fmt.Printf("blogs: %v\n", blogs) 48 | var res blog.FollowBlogRresp 49 | res.List = blogs 50 | res.MinTime = "0" 51 | if len(zSet) > 0 { 52 | res.MinTime = strconv.FormatInt(int64(zSet[len(zSet)-1].Score), 10) 53 | } 54 | // 取最小分数的记录数 55 | var offset int64 = 0 56 | minScore := zSet[len(zSet)-1].Score 57 | for _, element := range zSet { 58 | if element.Score == minScore { 59 | offset++ 60 | } 61 | } 62 | res.Offset = offset 63 | return &res, nil 64 | } 65 | -------------------------------------------------------------------------------- /biz/service/blog/get_follow_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog "xzdp/biz/model/blog" 10 | ) 11 | 12 | func TestGetFollowBlogService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewGetFollowBlogService(ctx, c) 16 | // init req and assert value 17 | req := &blog.FollowBlogReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog/get_hot_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | 6 | "xzdp/biz/dal/mysql" 7 | blog "xzdp/biz/model/blog" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/hlog" 11 | ) 12 | 13 | type GetHotBlogService struct { 14 | RequestContext *app.RequestContext 15 | Context context.Context 16 | } 17 | 18 | func NewGetHotBlogService(Context context.Context, RequestContext *app.RequestContext) *GetHotBlogService { 19 | return &GetHotBlogService{RequestContext: RequestContext, Context: Context} 20 | } 21 | 22 | func (h *GetHotBlogService) Run(req *blog.BlogReq) (resp *[]*blog.Blog, err error) { 23 | defer func() { 24 | hlog.CtxInfof(h.Context, "req = %+v", req) 25 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 26 | }() 27 | // todo edit your code 28 | 29 | blogList, err := mysql.QueryHotBlog(h.Context, int(req.Current)) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return &blogList, nil 34 | } 35 | -------------------------------------------------------------------------------- /biz/service/blog/get_hot_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | blog "xzdp/biz/model/blog" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/test/assert" 11 | ) 12 | 13 | func TestGetHotBlogService_Run(t *testing.T) { 14 | ctx := context.Background() 15 | c := app.NewContext(1) 16 | s := NewGetHotBlogService(ctx, c) 17 | // init req and assert value 18 | req := &blog.BlogReq{} 19 | resp, err := s.Run(req) 20 | assert.DeepEqual(t, nil, resp) 21 | assert.DeepEqual(t, nil, err) 22 | // todo edit your unit test. 23 | } 24 | -------------------------------------------------------------------------------- /biz/service/blog/get_likes.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/cloudwego/hertz/pkg/app" 7 | "xzdp/biz/dal/mysql" 8 | "xzdp/biz/dal/redis" 9 | user "xzdp/biz/model/user" 10 | "xzdp/biz/pkg/constants" 11 | ) 12 | 13 | type GetLikesService struct { 14 | RequestContext *app.RequestContext 15 | Context context.Context 16 | } 17 | 18 | func NewGetLikesService(Context context.Context, RequestContext *app.RequestContext) *GetLikesService { 19 | return &GetLikesService{RequestContext: RequestContext, Context: Context} 20 | } 21 | 22 | func (h *GetLikesService) Run(req *string) (resp *[]*user.UserDTO, err error) { 23 | //defer func() { 24 | // hlog.CtxInfof(h.Context, "req = %+v", req) 25 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 26 | //}() 27 | // todo edit your code 28 | key := constants.BLOG_LIKED_KEY + *req 29 | ids, err := redis.RedisClient.ZRange(h.Context, key, 0, 4).Result() 30 | if err != nil { 31 | return nil, err 32 | } 33 | var users []*user.User 34 | if !errors.Is(mysql.DB.Where("id in ?", ids).Find(&users).Error, nil) { 35 | return nil, errors.New("获取失败") 36 | } 37 | var userDtos []*user.UserDTO 38 | for _, u := range users { 39 | d := &user.UserDTO{ 40 | ID: u.ID, 41 | NickName: u.NickName, 42 | Icon: u.Icon, 43 | } 44 | userDtos = append(userDtos, d) 45 | } 46 | if len(userDtos) == 0 { 47 | userDtos = make([]*user.UserDTO, 0) 48 | } 49 | return &userDtos, nil 50 | } 51 | -------------------------------------------------------------------------------- /biz/service/blog/get_likes_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | user "xzdp/biz/model/user" 10 | ) 11 | 12 | func TestGetLikesService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewGetLikesService(ctx, c) 16 | // init req and assert value 17 | req := &string{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog/get_user_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "xzdp/biz/dal/mysql" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | blog "xzdp/biz/model/blog" 9 | "xzdp/biz/model/user" 10 | ) 11 | 12 | type GetUserBlogService struct { 13 | RequestContext *app.RequestContext 14 | Context context.Context 15 | } 16 | 17 | func NewGetUserBlogService(Context context.Context, RequestContext *app.RequestContext) *GetUserBlogService { 18 | return &GetUserBlogService{RequestContext: RequestContext, Context: Context} 19 | } 20 | 21 | func (h *GetUserBlogService) Run(req *blog.BlogReq, uerID int64) (resp *[]*blog.Blog, err error) { 22 | //defer func() { 23 | // hlog.CtxInfof(h.Context, "req = %+v", req) 24 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 25 | //}() 26 | // todo edit your code 27 | u, err := mysql.GetById(h.Context, uerID) 28 | if err != nil { 29 | return nil, err 30 | } 31 | d := &user.UserDTO{ 32 | ID: u.ID, 33 | NickName: u.NickName, 34 | Icon: u.Icon, 35 | } 36 | blogList, err := mysql.QueryBlogByUserID(h.Context, int(req.Current), d) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return &blogList, nil 41 | } 42 | -------------------------------------------------------------------------------- /biz/service/blog/get_user_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog "xzdp/biz/model/blog" 10 | ) 11 | 12 | func TestGetUserBlogService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewGetUserBlogService(ctx, c) 16 | // init req and assert value 17 | req := &blog.BlogReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog/like_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/cloudwego/hertz/pkg/common/hlog" 8 | redis2 "github.com/go-redis/redis/v8" 9 | "gorm.io/gorm" 10 | "strconv" 11 | "time" 12 | "xzdp/biz/dal/mysql" 13 | "xzdp/biz/dal/redis" 14 | "xzdp/biz/model/message" 15 | "xzdp/biz/pkg/constants" 16 | "xzdp/biz/utils" 17 | 18 | "github.com/cloudwego/hertz/pkg/app" 19 | blog "xzdp/biz/model/blog" 20 | ) 21 | 22 | type LikeBlogService struct { 23 | RequestContext *app.RequestContext 24 | Context context.Context 25 | } 26 | 27 | func NewLikeBlogService(Context context.Context, RequestContext *app.RequestContext) *LikeBlogService { 28 | return &LikeBlogService{RequestContext: RequestContext, Context: Context} 29 | } 30 | 31 | func (h *LikeBlogService) Run(req *string) (resp *blog.LikeResp, err error) { 32 | //defer func() { 33 | // hlog.CtxInfof(h.Context, "req = %+v", req) 34 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 35 | //}() 36 | // todo edit your code 37 | // 获取博客 38 | var interBlog blog.Blog 39 | err = mysql.DB.Where("id=?", req).First(&interBlog).Error 40 | if !errors.Is(err, nil) { 41 | return nil, errors.New("博客不存在") 42 | } 43 | // 判断是否已经点赞 44 | u := utils.GetUser(h.Context).GetID() 45 | idStr := strconv.FormatInt(u, 10) 46 | key := constants.BLOG_LIKED_KEY + *req 47 | isLike, err := redis.IsLiked(h.Context, key, idStr) 48 | if err != nil { 49 | hlog.Debugf("like redis error: %+v", err) 50 | return nil, err 51 | } 52 | fmt.Printf("isLike = %+v", isLike) 53 | // 如果已经点赞则取消点赞 54 | if isLike { 55 | if !errors.Is(redis.RedisClient.ZRem(h.Context, key, idStr).Err(), nil) { 56 | return nil, errors.New("取消点赞失败") 57 | } 58 | // 同步减少点赞数 59 | mysql.DB.Model(&blog.Blog{}).Where("id = ?", req).UpdateColumn("liked", gorm.Expr("liked - ?", 1)) 60 | return &blog.LikeResp{IsLiked: false}, nil 61 | } 62 | // 否则点赞 63 | if !errors.Is(redis.RedisClient.ZAdd(h.Context, key, &redis2.Z{ 64 | Score: float64(time.Now().Unix()), 65 | Member: idStr, 66 | }).Err(), nil) { 67 | return nil, errors.New("点赞失败") 68 | } 69 | // 同步增加点赞数 70 | mysql.DB.Model(&blog.Blog{}).Where("id = ?", req).UpdateColumn("liked", gorm.Expr("liked + ?", 1)) 71 | // 推送消息 72 | streamKey := constants.MESSAGE_STREAM_KEY + strconv.FormatInt(interBlog.UserId, 10) 73 | msg := &message.Message{ 74 | From: u, 75 | To: interBlog.UserId, 76 | Content: "点赞了你的博客", 77 | Type: "like", 78 | Time: time.Now().Format("2006-01-02 15:04:05"), 79 | } 80 | err = redis.ProduceMq(h.Context, streamKey, msg) 81 | if err != nil { 82 | return nil, err 83 | } 84 | return &blog.LikeResp{IsLiked: true}, nil 85 | } 86 | -------------------------------------------------------------------------------- /biz/service/blog/like_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog "xzdp/biz/model/blog" 10 | ) 11 | 12 | func TestLikeBlogService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewLikeBlogService(ctx, c) 16 | // init req and assert value 17 | req := &string{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog/post_blog.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | redis2 "github.com/go-redis/redis/v8" 7 | "strconv" 8 | "time" 9 | "xzdp/biz/dal/mysql" 10 | "xzdp/biz/dal/redis" 11 | "xzdp/biz/pkg/constants" 12 | "xzdp/biz/utils" 13 | 14 | "github.com/cloudwego/hertz/pkg/app" 15 | blog "xzdp/biz/model/blog" 16 | ) 17 | 18 | type PostBlogService struct { 19 | RequestContext *app.RequestContext 20 | Context context.Context 21 | } 22 | 23 | func NewPostBlogService(Context context.Context, RequestContext *app.RequestContext) *PostBlogService { 24 | return &PostBlogService{RequestContext: RequestContext, Context: Context} 25 | } 26 | 27 | func (h *PostBlogService) Run(req *blog.Blog) (resp *blog.Blog, err error) { 28 | //defer func() { 29 | // hlog.CtxInfof(h.Context, "req = %+v", req) 30 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 31 | //}() 32 | // todo edit your code 33 | u := utils.GetUser(h.Context).GetID() 34 | req.UserId = u 35 | if !errors.Is(mysql.DB.Create(&req).Error, nil) { 36 | return nil, errors.New("创建失败") 37 | } 38 | req.Icon = utils.GetUser(h.Context).GetIcon() 39 | req.NickName = utils.GetUser(h.Context).GetNickName() 40 | req.IsLiked = false 41 | fans, err := mysql.GetFansByID(h.Context, u) 42 | if err != nil { 43 | return nil, err 44 | } 45 | for _, fan := range fans { 46 | key := constants.FEED_KEY + strconv.FormatInt(fan.ID, 10) 47 | err = redis.RedisClient.ZAdd(h.Context, key, &redis2.Z{ 48 | Score: float64(time.Now().Unix()), 49 | Member: req.ID, 50 | }).Err() 51 | } 52 | return req, nil 53 | } 54 | -------------------------------------------------------------------------------- /biz/service/blog/post_blog_test.go: -------------------------------------------------------------------------------- 1 | package blog 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog "xzdp/biz/model/blog" 10 | ) 11 | 12 | func TestPostBlogService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewPostBlogService(ctx, c) 16 | // init req and assert value 17 | req := &blog.BlogReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog_comment/delete_comment.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | blog_comment "xzdp/biz/model/blog_comment" 8 | ) 9 | 10 | type DeleteCommentService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewDeleteCommentService(Context context.Context, RequestContext *app.RequestContext) *DeleteCommentService { 16 | return &DeleteCommentService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *DeleteCommentService) Run(req *string) (resp *blog_comment.Empty, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/blog_comment/delete_comment_test.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | ) 10 | 11 | func TestDeleteCommentService_Run(t *testing.T) { 12 | ctx := context.Background() 13 | c := app.NewContext(1) 14 | s := NewDeleteCommentService(ctx, c) 15 | // init req and assert value 16 | req := &string{} 17 | resp, err := s.Run(req) 18 | assert.DeepEqual(t, nil, resp) 19 | assert.DeepEqual(t, nil, err) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/service/blog_comment/get_comment.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | blog_comment "xzdp/biz/model/blog_comment" 8 | ) 9 | 10 | type GetCommentService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewGetCommentService(Context context.Context, RequestContext *app.RequestContext) *GetCommentService { 16 | return &GetCommentService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *GetCommentService) Run(req *blog_comment.CommentReq) (resp *[]*blog_comment.BlogComment, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/blog_comment/get_comment_test.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog_comment "xzdp/biz/model/blog_comment" 10 | ) 11 | 12 | func TestGetCommentService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewGetCommentService(ctx, c) 16 | // init req and assert value 17 | req := &blog_comment.CommentReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog_comment/get_hot_comment.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | blog_comment "xzdp/biz/model/blog_comment" 8 | ) 9 | 10 | type GetHotCommentService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewGetHotCommentService(Context context.Context, RequestContext *app.RequestContext) *GetHotCommentService { 16 | return &GetHotCommentService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *GetHotCommentService) Run(req *blog_comment.CommentReq) (resp *[]*blog_comment.BlogComment, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/blog_comment/get_hot_comment_test.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog_comment "xzdp/biz/model/blog_comment" 10 | ) 11 | 12 | func TestGetHotCommentService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewGetHotCommentService(ctx, c) 16 | // init req and assert value 17 | req := &blog_comment.CommentReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/blog_comment/like_comment.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | blog_comment "xzdp/biz/model/blog_comment" 8 | ) 9 | 10 | type LikeCommentService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewLikeCommentService(Context context.Context, RequestContext *app.RequestContext) *LikeCommentService { 16 | return &LikeCommentService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *LikeCommentService) Run(req *string) (resp *blog_comment.LikeResp, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/blog_comment/like_comment_test.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | ) 10 | 11 | func TestLikeCommentService_Run(t *testing.T) { 12 | ctx := context.Background() 13 | c := app.NewContext(1) 14 | s := NewLikeCommentService(ctx, c) 15 | // init req and assert value 16 | req := &string{} 17 | resp, err := s.Run(req) 18 | assert.DeepEqual(t, nil, resp) 19 | assert.DeepEqual(t, nil, err) 20 | // todo edit your unit test. 21 | } 22 | -------------------------------------------------------------------------------- /biz/service/blog_comment/post_comment.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | blog_comment "xzdp/biz/model/blog_comment" 8 | ) 9 | 10 | type PostCommentService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewPostCommentService(Context context.Context, RequestContext *app.RequestContext) *PostCommentService { 16 | return &PostCommentService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *PostCommentService) Run(req *blog_comment.BlogComment) (resp *blog_comment.BlogComment, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/blog_comment/post_comment_test.go: -------------------------------------------------------------------------------- 1 | package blog_comment 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | blog_comment "xzdp/biz/model/blog_comment" 10 | ) 11 | 12 | func TestPostCommentService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewPostCommentService(ctx, c) 16 | // init req and assert value 17 | req := &blog_comment.BlogComment{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/follow/common_follow.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | "xzdp/biz/dal/mysql" 8 | "xzdp/biz/dal/redis" 9 | model "xzdp/biz/model/user" 10 | "xzdp/biz/pkg/constants" 11 | "xzdp/biz/utils" 12 | 13 | "github.com/cloudwego/hertz/pkg/app" 14 | follow "xzdp/biz/model/follow" 15 | ) 16 | 17 | type CommonFollowService struct { 18 | RequestContext *app.RequestContext 19 | Context context.Context 20 | } 21 | 22 | func NewCommonFollowService(Context context.Context, RequestContext *app.RequestContext) *CommonFollowService { 23 | return &CommonFollowService{RequestContext: RequestContext, Context: Context} 24 | } 25 | 26 | func (h *CommonFollowService) Run(targetUserID string) (resp *follow.CommonFollowResp, err error) { 27 | //defer func() { 28 | // hlog.CtxInfof(h.Context, "req = %+v", req) 29 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 30 | //}() 31 | // todo edit your code 32 | //获取当前用户ID 33 | user := utils.GetUser(h.Context).GetID() 34 | key1 := constants.FOLLOW_USER_KEY + strconv.FormatInt(user, 10) 35 | key2 := constants.FOLLOW_USER_KEY + targetUserID 36 | arr, err := redis.RedisClient.SInter(h.Context, key1, key2).Result() 37 | if err != nil { 38 | return nil, err 39 | } 40 | var users []*model.User 41 | if !errors.Is(mysql.DB.Where("id in ?", arr).Find(&users).Error, nil) { 42 | return nil, errors.New("查询失败") 43 | } 44 | var userDto []*model.UserDTO 45 | // 遍历arr,转换为userDTO 46 | for _, u := range users { 47 | d := utils.UserToUserDTO(u) 48 | userDto = append(userDto, d) 49 | } 50 | return &follow.CommonFollowResp{ 51 | CommonFollows: userDto, 52 | }, nil 53 | } 54 | -------------------------------------------------------------------------------- /biz/service/follow/common_follow_test.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | follow "xzdp/biz/model/follow" 10 | ) 11 | 12 | func TestCommonFollowService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewCommonFollowService(ctx, c) 16 | // init req and assert value 17 | req := &follow.IsFollowedReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/follow/follow.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/cloudwego/hertz/pkg/app" 7 | "github.com/cloudwego/hertz/pkg/common/hlog" 8 | "strconv" 9 | "xzdp/biz/dal/mysql" 10 | "xzdp/biz/dal/redis" 11 | follow "xzdp/biz/model/follow" 12 | "xzdp/biz/pkg/constants" 13 | "xzdp/biz/utils" 14 | ) 15 | 16 | type FollowService struct { 17 | RequestContext *app.RequestContext 18 | Context context.Context 19 | } 20 | 21 | func NewFollowService(Context context.Context, RequestContext *app.RequestContext) *FollowService { 22 | return &FollowService{RequestContext: RequestContext, Context: Context} 23 | } 24 | 25 | func (h *FollowService) Run(req *follow.FollowReq) (resp *follow.FollowResp, err error) { 26 | //defer func() { 27 | // hlog.CtxInfof(h.Context, "req = %+v", req) 28 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 29 | //}() 30 | // todo edit your code 31 | myID := utils.GetUser(h.Context).GetID() 32 | isFollow := req.GetIsFollow() 33 | targetUserId := req.GetTargetUser() 34 | f := follow.Follow{ 35 | UserId: myID, 36 | FollowUserId: targetUserId, 37 | } 38 | // 如果是true,则添加关注,将用户id和被关注用户的id存入数据库 39 | if isFollow { 40 | // 判断是否已经关注 41 | if !errors.Is(redis.RedisClient.SIsMember(h.Context, constants.FOLLOW_USER_KEY+strconv.FormatInt(myID, 10), targetUserId).Err(), nil) { 42 | return nil, errors.New("关注失败") 43 | } 44 | // 将关注的用户存入redis的set中 45 | if !errors.Is(redis.RedisClient.SAdd(h.Context, constants.FOLLOW_USER_KEY+strconv.FormatInt(myID, 10), targetUserId).Err(), nil) { 46 | hlog.CtxErrorf(h.Context, "err = %v", err) 47 | return nil, err 48 | } 49 | if !errors.Is(mysql.DB.Create(&f).Error, nil) { 50 | return nil, errors.New("关注失败") 51 | } 52 | return &follow.FollowResp{RespBody: &f}, nil 53 | } 54 | // 如果是false,则取消关注 55 | if !errors.Is(mysql.DB.Where("user_id = ? and follow_user_id = ?", myID, targetUserId).Delete(&f).Error, nil) { 56 | return nil, errors.New("取消关注失败") 57 | } 58 | // 将取消关注的用户从redis的set中删除 59 | if !errors.Is(redis.RedisClient.SRem(h.Context, constants.FOLLOW_USER_KEY+strconv.FormatInt(myID, 10), targetUserId).Err(), nil) { 60 | hlog.CtxErrorf(h.Context, "err = %v", err) 61 | return nil, err 62 | } 63 | return &follow.FollowResp{RespBody: &f}, nil 64 | } 65 | -------------------------------------------------------------------------------- /biz/service/follow/follow_test.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | follow "xzdp/biz/model/follow" 10 | ) 11 | 12 | func TestFollowService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewFollowService(ctx, c) 16 | // init req and assert value 17 | req := &follow.FollowReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/follow/is_followed.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | "xzdp/biz/dal/redis" 8 | "xzdp/biz/pkg/constants" 9 | "xzdp/biz/utils" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | follow "xzdp/biz/model/follow" 13 | ) 14 | 15 | type IsFollowedService struct { 16 | RequestContext *app.RequestContext 17 | Context context.Context 18 | } 19 | 20 | func NewIsFollowedService(Context context.Context, RequestContext *app.RequestContext) *IsFollowedService { 21 | return &IsFollowedService{RequestContext: RequestContext, Context: Context} 22 | } 23 | 24 | func (h *IsFollowedService) Run(targetUserID string) (resp *follow.IsFollowedResp, err error) { 25 | //defer func() { 26 | // hlog.CtxInfof(h.Context, "req = %+v", req) 27 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 28 | //}() 29 | // todo edit your code 30 | //获取当前用户ID 31 | user := utils.GetUser(h.Context).GetID() 32 | //查找是否关注 33 | if !errors.Is(redis.RedisClient.SIsMember(h.Context, constants.FOLLOW_USER_KEY+strconv.FormatInt(user, 10), targetUserID).Err(), nil) { 34 | return &follow.IsFollowedResp{IsFollowed: false}, nil 35 | } 36 | return &follow.IsFollowedResp{IsFollowed: true}, nil 37 | } 38 | -------------------------------------------------------------------------------- /biz/service/follow/is_followed_test.go: -------------------------------------------------------------------------------- 1 | package follow 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | follow "xzdp/biz/model/follow" 10 | ) 11 | 12 | func TestIsFollowedService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewIsFollowedService(ctx, c) 16 | // init req and assert value 17 | req := &follow.IsFollowedReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/image/upload.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | image "xzdp/biz/model/image" 8 | ) 9 | 10 | type UploadService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewUploadService(Context context.Context, RequestContext *app.RequestContext) *UploadService { 16 | return &UploadService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *UploadService) Run(req *[]byte) (resp *image.UploadResp, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return nil, nil 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/image/upload_test.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | image "xzdp/biz/model/image" 10 | ) 11 | 12 | func TestUploadService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewUploadService(ctx, c) 16 | // init req and assert value 17 | req := &[]byte{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/message/sse.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/cloudwego/hertz/pkg/common/hlog" 7 | redis2 "github.com/go-redis/redis/v8" 8 | "strconv" 9 | "xzdp/biz/dal/redis" 10 | "xzdp/biz/pkg/cache" 11 | "xzdp/biz/pkg/constants" 12 | "xzdp/biz/utils" 13 | 14 | "github.com/cloudwego/hertz/pkg/app" 15 | message "xzdp/biz/model/message" 16 | ) 17 | 18 | type SseService struct { 19 | RequestContext *app.RequestContext 20 | Context context.Context 21 | } 22 | 23 | func NewSseService(Context context.Context, RequestContext *app.RequestContext) *SseService { 24 | return &SseService{RequestContext: RequestContext, Context: Context} 25 | } 26 | 27 | func (h *SseService) Run(req string) (resp *string, err error) { 28 | defer func() { 29 | hlog.CtxInfof(h.Context, "req = %+v", req) 30 | hlog.CtxInfof(h.Context, "resp = %+v", resp) 31 | }() 32 | xSet, err := h.consumeMq(">") 33 | // 有更新的话,发送给前端 34 | if err != nil { 35 | if errors.Is(err, redis2.Nil) { 36 | hlog.CtxDebugf(h.Context, "No messages found in Redis") 37 | return nil, nil 38 | } 39 | hlog.CtxErrorf(h.Context, "redis.ConsumeMq err = %+v", err) 40 | return nil, err 41 | } 42 | msgResp, err := h.handleMessage(xSet) 43 | if err != nil { 44 | hlog.CtxErrorf(h.Context, "handleMessage failed with data: %v, error: %v", xSet, err) 45 | return nil, err 46 | } 47 | bytes, err := utils.SerializeStruct(msgResp) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return &bytes, nil 52 | } 53 | 54 | func (h *SseService) consumeMq(id string) ([]redis2.XStream, error) { 55 | u := utils.GetUser(h.Context).GetID() 56 | idStr := strconv.FormatInt(u, 10) 57 | key := constants.MESSAGE_STREAM_KEY + idStr 58 | consumer := constants.STREAM_CONSUMER + idStr 59 | redis.CreateConsumerGroup(h.Context, key) 60 | // 读redis消息队列 61 | xSet, err := redis.ConsumeMq(h.Context, key, consumer, 20, 1, id) 62 | return xSet, err 63 | } 64 | 65 | func (h *SseService) Ack(id string) error { 66 | u := utils.GetUser(h.Context).GetID() 67 | idStr := strconv.FormatInt(u, 10) 68 | key := constants.MESSAGE_STREAM_KEY + idStr 69 | err := redis.AckMq(h.Context, key, id) 70 | if err != nil { 71 | return err 72 | } 73 | return nil 74 | } 75 | func (h *SseService) handleMessage(xSet []redis2.XStream) (*message.MessageResp, error) { 76 | if len(xSet) == 0 { 77 | return nil, nil 78 | } 79 | m := xSet[0].Messages 80 | if len(m) == 0 { 81 | return nil, nil 82 | } 83 | hlog.CtxDebugf(h.Context, "xSet = %+v", xSet) 84 | var msg message.Message 85 | jsonStr := m[0].Values["message"].(string) 86 | if err := utils.UnSerializeStruct(jsonStr, &msg); err != nil { 87 | hlog.CtxErrorf(h.Context, "MapToStructByJson failed with data: %v, error: %v", m[0].Values, err) 88 | return nil, err 89 | } 90 | hlog.CtxDebugf(h.Context, "msg = %+v", msg) 91 | to := msg.To 92 | userDto, err := cache.GetUserDtoFromCacheOrDB(h.Context, to) 93 | if err != nil { 94 | hlog.CtxErrorf(h.Context, "GetUserDtoFromCacheOrDB failed with data: %v, error: %v", to, err) 95 | return nil, err 96 | } 97 | msgResp := &message.MessageResp{ 98 | Message: &msg, 99 | User: userDto, 100 | } 101 | err = h.Ack(m[0].ID) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return msgResp, nil 106 | } 107 | -------------------------------------------------------------------------------- /biz/service/message/sse_test.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | message "xzdp/biz/model/message" 10 | ) 11 | 12 | func TestSseService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewSseService(ctx, c) 16 | // init req and assert value 17 | req := &message.Empty{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/shop/shop_info.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "xzdp/biz/dal/redis" 9 | "xzdp/biz/pkg/constants" 10 | 11 | shop "xzdp/biz/model/shop" 12 | 13 | "xzdp/biz/dal/mysql" 14 | 15 | "github.com/cloudwego/hertz/pkg/app" 16 | ) 17 | 18 | type ShopInfoService struct { 19 | RequestContext *app.RequestContext 20 | Context context.Context 21 | } 22 | 23 | func NewShopInfoService(Context context.Context, RequestContext *app.RequestContext) *ShopInfoService { 24 | return &ShopInfoService{RequestContext: RequestContext, Context: Context} 25 | } 26 | 27 | func (h *ShopInfoService) Run(id int64) (resp *shop.Shop, err error) { 28 | key := fmt.Sprintf("%s%d", constants.CACHE_SHOP_KEY, id) 29 | 30 | result, err := redis.GetStringLogical(h.Context, key, constants.CACHE_SHOP_TTL, WrappedQueryByID, h.Context, id) 31 | if err != nil || result == "" { 32 | return nil, err 33 | } 34 | 35 | err = json.Unmarshal([]byte(result), &resp) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | //hlog.Debugf("test! : %s", resp) 41 | 42 | return resp, nil 43 | } 44 | 45 | func WrappedQueryByID(args ...interface{}) (interface{}, error) { 46 | if len(args) != 2 { 47 | return nil, fmt.Errorf("incorrect number of arguments") 48 | } 49 | 50 | ctx, ok := args[0].(context.Context) 51 | if !ok { 52 | return nil, fmt.Errorf("first argument is not context.Context") 53 | } 54 | 55 | id, ok := args[1].(int64) 56 | if !ok { 57 | return nil, fmt.Errorf("second argument is not int") 58 | } 59 | 60 | result, err := mysql.QueryByID(ctx, id) 61 | return result, err 62 | } 63 | -------------------------------------------------------------------------------- /biz/service/shop/shop_info_test.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "testing" 7 | 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/hertz/pkg/common/test/assert" 10 | ) 11 | 12 | func TestShopInfoService_Run(t *testing.T) { 13 | var shopId int64 = 1 14 | ctx := context.Background() 15 | c := app.NewContext(1) 16 | s := NewShopInfoService(ctx, c) 17 | resp, err := s.Run(shopId) 18 | assert.NotEqual(t, nil, resp) 19 | assert.DeepEqual(t, nil, err) 20 | refShop := resp 21 | var wg sync.WaitGroup 22 | getShopInfo := func() { 23 | defer wg.Done() 24 | ctx := context.Background() 25 | c := app.NewContext(1) 26 | s := NewShopInfoService(ctx, c) 27 | for i := 0; i < 100; i++ { 28 | resp, err := s.Run(shopId) 29 | assert.DeepEqual(t, refShop, resp) 30 | assert.DeepEqual(t, nil, err) 31 | } 32 | } 33 | for i := 0; i < 10; i++ { 34 | wg.Add(1) 35 | go getShopInfo() 36 | } 37 | wg.Wait() 38 | // todo edit your unit test. 39 | } 40 | -------------------------------------------------------------------------------- /biz/service/shop/shop_list.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "log" 7 | "xzdp/biz/dal/redis" 8 | "xzdp/biz/pkg/constants" 9 | 10 | "xzdp/biz/dal/mysql" 11 | shop "xzdp/biz/model/shop" 12 | 13 | "github.com/cloudwego/hertz/pkg/app" 14 | "github.com/cloudwego/hertz/pkg/common/hlog" 15 | ) 16 | 17 | type ShopListService struct { 18 | RequestContext *app.RequestContext 19 | Context context.Context 20 | } 21 | 22 | func NewShopListService(Context context.Context, RequestContext *app.RequestContext) *ShopListService { 23 | return &ShopListService{RequestContext: RequestContext, Context: Context} 24 | } 25 | 26 | func (h *ShopListService) Run(req *shop.Empty) (resp *[]*shop.ShopType, err error) { 27 | defer func() { 28 | hlog.CtxInfof(h.Context, "req = %+v", req) 29 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 30 | }() 31 | 32 | // todo edit your code 33 | 34 | var shopTypeList []*shop.ShopType 35 | 36 | // 从 Redis 获取数据 37 | shopTypeJsonList, err := redis.RedisClient.LRange(h.Context, constants.CACHE_SHOP_TYPE_LIST_KEY, 0, -1).Result() 38 | if err == nil && len(shopTypeJsonList) > 0 { 39 | for _, shopTypeJson := range shopTypeJsonList { 40 | var shopType shop.ShopType 41 | err := json.Unmarshal([]byte(shopTypeJson), &shopType) 42 | if err != nil { 43 | hlog.CtxErrorf(h.Context, "Error unmarshalling shop type: %v", err) 44 | continue 45 | } 46 | shopTypeList = append(shopTypeList, &shopType) 47 | } 48 | return &shopTypeList, nil 49 | } 50 | 51 | hlog.CtxInfof(h.Context, "hello asdfas yangqi") 52 | 53 | // 如果 Redis 没有数据,从数据库获取数据 54 | shopTypeList, err = mysql.QueryShopType(h.Context) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | // 将数据存储到 Redis 60 | for _, shopType := range shopTypeList { 61 | shopTypeJson, err := json.Marshal(shopType) 62 | if err != nil { 63 | log.Printf("Error marshalling shop type: %v", err) 64 | continue 65 | } 66 | redis.RedisClient.RPush(h.Context, constants.CACHE_SHOP_TYPE_LIST_KEY, shopTypeJson) 67 | } 68 | 69 | return &shopTypeList, nil 70 | } 71 | -------------------------------------------------------------------------------- /biz/service/shop/shop_list_test.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | shop "xzdp/biz/model/shop" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/test/assert" 11 | ) 12 | 13 | func TestShopListService_Run(t *testing.T) { 14 | ctx := context.Background() 15 | c := app.NewContext(1) 16 | s := NewShopListService(ctx, c) 17 | // init req and assert value 18 | req := &shop.Empty{} 19 | resp, err := s.Run(req) 20 | assert.DeepEqual(t, nil, resp) 21 | assert.DeepEqual(t, nil, err) 22 | // todo edit your unit test. 23 | } 24 | -------------------------------------------------------------------------------- /biz/service/shop/shop_of_type.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | 6 | shop "xzdp/biz/model/shop" 7 | 8 | "xzdp/biz/dal/mysql" 9 | "xzdp/biz/pkg/constants" 10 | 11 | "github.com/cloudwego/hertz/pkg/app" 12 | ) 13 | 14 | type ShopOfTypeService struct { 15 | RequestContext *app.RequestContext 16 | Context context.Context 17 | } 18 | 19 | func NewShopOfTypeService(Context context.Context, RequestContext *app.RequestContext) *ShopOfTypeService { 20 | return &ShopOfTypeService{RequestContext: RequestContext, Context: Context} 21 | } 22 | 23 | func (h *ShopOfTypeService) Run(req *shop.ShopOfTypeReq) (resp *[]*shop.Shop, err error) { 24 | //defer func() { 25 | // hlog.CtxInfof(h.Context, "req = %+v", req) 26 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 27 | //}() 28 | // todo edit your code 29 | var shops []*shop.Shop 30 | err = mysql.DB.Where("type_id = ?", req.TypeId).Offset(int((req.Current - 1) * constants.DEFAULT_PAGE_SIZE)).Limit(constants.DEFAULT_PAGE_SIZE).Find(&shops).Error 31 | if err != nil { 32 | return nil, err 33 | } 34 | return &shops, nil 35 | } 36 | -------------------------------------------------------------------------------- /biz/service/shop/shop_of_type_geo.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | 6 | "xzdp/biz/dal/mysql" 7 | "xzdp/biz/dal/redis" 8 | shop "xzdp/biz/model/shop" 9 | 10 | "github.com/cloudwego/hertz/pkg/app" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | ) 13 | 14 | type ShopOfTypeGeoService struct { 15 | RequestContext *app.RequestContext 16 | Context context.Context 17 | } 18 | 19 | func NewShopOfTypeGeoService(Context context.Context, RequestContext *app.RequestContext) *ShopOfTypeGeoService { 20 | return &ShopOfTypeGeoService{RequestContext: RequestContext, Context: Context} 21 | } 22 | 23 | func (h *ShopOfTypeGeoService) Run(req *shop.ShopOfTypeGeoReq) (resp *[]*shop.Shop, err error) { 24 | defer func() { 25 | hlog.CtxInfof(h.Context, "req = %+v", req) 26 | hlog.CtxInfof(h.Context, "resp = %+v", resp) 27 | }() 28 | err = mysql.LoadShopListToCache(h.Context) 29 | if err != nil { 30 | return nil, err 31 | } 32 | shops, err := redis.QueryShopWithDistance(h.Context, req) 33 | if err != nil { 34 | return nil, err 35 | } 36 | resp, err = mysql.QueryAllShop(h.Context, shops) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return resp, nil 41 | } 42 | -------------------------------------------------------------------------------- /biz/service/shop/shop_of_type_geo_test.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | shop "xzdp/biz/model/shop" 10 | ) 11 | 12 | func TestShopOfTypeGeoService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewShopOfTypeGeoService(ctx, c) 16 | // init req and assert value 17 | req := &shop.ShopOfTypeGeoReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/shop/shop_of_type_test.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | shop "xzdp/biz/model/shop" 10 | ) 11 | 12 | func TestShopOfTypeService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewShopOfTypeService(ctx, c) 16 | // init req and assert value 17 | req := &shop.ShopOfTypeReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/user/send_code.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "time" 7 | "xzdp/biz/dal/redis" 8 | user "xzdp/biz/model/user" 9 | "xzdp/biz/pkg/constants" 10 | "xzdp/biz/utils" 11 | 12 | "github.com/cloudwego/hertz/pkg/app" 13 | "github.com/cloudwego/hertz/pkg/common/hlog" 14 | ) 15 | 16 | type SendCodeService struct { 17 | RequestContext *app.RequestContext 18 | Context context.Context 19 | } 20 | 21 | func NewSendCodeService(Context context.Context, RequestContext *app.RequestContext) *SendCodeService { 22 | return &SendCodeService{RequestContext: RequestContext, Context: Context} 23 | } 24 | 25 | func (h *SendCodeService) Run(req *user.UserLoginFrom) (resp *user.Result, err error) { 26 | defer func() { 27 | hlog.CtxInfof(h.Context, "req = %+v", req) 28 | hlog.CtxInfof(h.Context, "resp = %+v", resp) 29 | }() 30 | // todo edit your code 31 | phone := req.Phone 32 | 33 | if !utils.ValidateMobile(phone) { 34 | return nil, errors.New("phone isn't validate") 35 | } 36 | if phone == "" { 37 | return nil, errors.New("phone can't be empty") 38 | } 39 | 40 | code := utils.GenerateDigits(6) 41 | err = redis.RedisClient.Set(h.Context, constants.LOGIN_CODE_KEY+phone, code, constants.LOGIN_CODE_EXPIRE*time.Second).Err() // add expiration 42 | if err != nil { 43 | hlog.CtxErrorf(h.Context, "err = %s", err.Error()) 44 | return nil, err 45 | } 46 | 47 | hlog.CtxInfof(h.Context, "code = %s", code) 48 | return &user.Result{Success: true}, nil 49 | } 50 | -------------------------------------------------------------------------------- /biz/service/user/send_code_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "testing" 7 | 8 | mysql2 "xzdp/biz/dal/mysql" 9 | 10 | "gorm.io/driver/mysql" 11 | 12 | redis2 "xzdp/biz/dal/redis" 13 | user "xzdp/biz/model/user" 14 | 15 | "github.com/DATA-DOG/go-sqlmock" 16 | "github.com/alicebob/miniredis/v2" 17 | "github.com/cloudwego/hertz/pkg/app" 18 | "github.com/cloudwego/hertz/pkg/common/test/assert" 19 | "github.com/go-redis/redis/v8" 20 | "gorm.io/gorm" 21 | ) 22 | 23 | var ( 24 | mockDB sqlmock.Sqlmock 25 | db *sql.DB 26 | ) 27 | 28 | func TestSendCodeService_Run(t *testing.T) { 29 | ctx := context.Background() 30 | c := app.NewContext(1) 31 | s := NewSendCodeService(ctx, c) 32 | // init req and assert value 33 | req := &user.UserLoginFrom{ 34 | Phone: "12345678901"} 35 | resp, err := s.Run(req) 36 | assert.Nil(t, resp) 37 | assert.NotNil(t, err) 38 | 39 | resp, err = s.Run(&user.UserLoginFrom{ 40 | Phone: "13412332123"}) 41 | 42 | assert.DeepEqual(t, &user.Result{Success: true}, resp) 43 | assert.DeepEqual(t, nil, err) 44 | } 45 | 46 | func TestMain(m *testing.M) { 47 | s, err := miniredis.Run() 48 | if err != nil { 49 | panic(err) 50 | } 51 | defer s.Close() 52 | redis2.RedisClient = redis.NewClient(&redis.Options{ 53 | Addr: s.Addr(), // mock redis server的地址 54 | }) 55 | var mockErr error 56 | db, mockDB, mockErr = sqlmock.New() 57 | if mockErr != nil { 58 | panic(mockErr) 59 | } 60 | defer db.Close() 61 | 62 | mysql2.DB, err = gorm.Open(mysql.New(mysql.Config{ 63 | Conn: db, 64 | SkipInitializeWithVersion: true, // 跳过版本检查以支持 mock 65 | }), &gorm.Config{}) 66 | if err != nil { 67 | panic(err) 68 | } 69 | m.Run() 70 | } 71 | -------------------------------------------------------------------------------- /biz/service/user/user_info.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "xzdp/biz/dal/mysql" 8 | user "xzdp/biz/model/user" 9 | 10 | "github.com/cloudwego/hertz/pkg/app" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | ) 13 | 14 | type UserInfoService struct { 15 | RequestContext *app.RequestContext 16 | Context context.Context 17 | } 18 | 19 | func NewUserInfoService(Context context.Context, RequestContext *app.RequestContext) *UserInfoService { 20 | return &UserInfoService{RequestContext: RequestContext, Context: Context} 21 | } 22 | 23 | func (h *UserInfoService) Run(req *user.UserLoginFrom, c *app.RequestContext) (resp *user.UserInfo, err error) { 24 | //defer func() { 25 | // hlog.CtxInfof(h.Context, "req = %+v", req) 26 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 27 | //}() 28 | // todo edit your code 29 | strId := c.Param("id") 30 | id, err := strconv.ParseInt(strId, 10, 64) 31 | if err != nil { 32 | return nil, err 33 | } 34 | userInfo, err := mysql.GetUserInfoById(h.Context, id) 35 | if err == nil && userInfo != nil { 36 | return userInfo, nil 37 | } 38 | 39 | err = h.createNewUserWithId(id) 40 | if err != nil { 41 | return nil, err 42 | } 43 | userInfo, err = mysql.GetUserInfoById(h.Context, id) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return userInfo, nil 48 | } 49 | 50 | func (h *UserInfoService) createNewUserWithId(id int64) error { 51 | user := user.UserInfo{ 52 | UserId: id, 53 | } 54 | result := mysql.DB.Create(&user) 55 | hlog.CtxDebugf(h.Context, "result = %+v", result) 56 | return result.Error 57 | } 58 | -------------------------------------------------------------------------------- /biz/service/user/user_info_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | model "xzdp/biz/model/user" 7 | 8 | "github.com/DATA-DOG/go-sqlmock" 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/test/assert" 11 | "github.com/cloudwego/hertz/pkg/route/param" 12 | ) 13 | 14 | // 编写测试用例 15 | func TestUserInfoService_Run(t *testing.T) { 16 | // 创建一个实例 of UserInfoService, MockDB 和 UserLoginFrom 17 | h := NewUserInfoService(context.Background(), nil) 18 | 19 | req := &model.UserLoginFrom{ 20 | Phone: "1234567890", 21 | } 22 | 23 | expectedUser := model.UserInfo{ 24 | UserId: 1, 25 | } 26 | 27 | mockDB.ExpectQuery("SELECT \\* FROM `tb_user_info` WHERE user_id = \\? ORDER BY `tb_user_info`.`user_id` LIMIT \\?"). 28 | WithArgs(1, 1). 29 | WillReturnRows(sqlmock.NewRows([]string{"user_id", "phone"}).AddRow(1, "1234567890")) 30 | 31 | c := app.RequestContext{ 32 | Params: param.Params{param.Param{"id", "1"}}, 33 | } 34 | 35 | resp, err := h.Run(req, &c) 36 | 37 | assert.DeepEqual(t, &expectedUser, resp) 38 | assert.DeepEqual(t, nil, err) 39 | 40 | // 如果需要,可以添加更多的测试用例来覆盖不同的场景,例如用户未找到的情况 41 | } 42 | -------------------------------------------------------------------------------- /biz/service/user/user_login.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "xzdp/biz/dal/mysql" 9 | "xzdp/biz/dal/redis" 10 | model "xzdp/biz/model/user" 11 | "xzdp/biz/pkg/constants" 12 | "xzdp/biz/utils" 13 | 14 | "github.com/cloudwego/hertz/pkg/app" 15 | "github.com/cloudwego/hertz/pkg/common/hlog" 16 | "github.com/jinzhu/copier" 17 | ) 18 | 19 | type UserLoginService struct { 20 | RequestContext *app.RequestContext 21 | Context context.Context 22 | } 23 | 24 | func NewUserLoginService(Context context.Context, RequestContext *app.RequestContext) *UserLoginService { 25 | return &UserLoginService{RequestContext: RequestContext, Context: Context} 26 | } 27 | 28 | func (h *UserLoginService) Run(req *model.UserLoginFrom) (resp *model.Result, err error) { 29 | defer func() { 30 | hlog.CtxInfof(h.Context, "req = %+v", req) 31 | hlog.CtxInfof(h.Context, "resp = %+v", resp) 32 | }() 33 | // todo edit your code 34 | phone := req.Phone 35 | code := req.Code 36 | if phone == "" || code == "" { 37 | return nil, errors.New("phone or code can't be empty") 38 | } 39 | redisCode, err := redis.RedisClient.Get(h.Context, constants.LOGIN_CODE_KEY+phone).Result() 40 | if err != nil { 41 | hlog.CtxErrorf(h.Context, "err = %s", err.Error()) 42 | return nil, err 43 | } 44 | if redisCode != code { 45 | return nil, fmt.Errorf("code not match") 46 | } 47 | 48 | token, err := utils.RandomUUID() 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | var user model.User 54 | result := mysql.DB.Debug().First(&user, "phone = ?", phone) 55 | hlog.CtxInfof(h.Context, "result = %+v", result) 56 | 57 | if result.Error != nil { 58 | user, err = h.createNewUserWithPhone(phone) 59 | if err != nil { 60 | return nil, err 61 | } 62 | } 63 | 64 | var userdto model.UserDTO 65 | copier.Copy(&userdto, &user) 66 | if err = redis.RedisClient.HMSet(h.Context, constants.LOGIN_USER_KEY+token, map[string]interface{}{ 67 | "id": userdto.ID, 68 | "nick_name": userdto.NickName, 69 | "icon": userdto.Icon, 70 | }).Err(); err != nil { 71 | hlog.CtxErrorf(h.Context, "err = %s", err.Error()) 72 | hlog.CtxErrorf(h.Context, "userdto = %+v", userdto) 73 | return nil, err 74 | } 75 | 76 | return &model.Result{Success: true, Data: &token}, nil 77 | } 78 | 79 | func (h *UserLoginService) createNewUserWithPhone(phone string) (model.User, error) { 80 | user := model.User{ 81 | Phone: phone, 82 | NickName: "user_" + utils.RandomString(10), 83 | } 84 | 85 | result := mysql.DB.Debug().Create(&user) 86 | hlog.CtxInfof(h.Context, "result = %+v", result) 87 | return user, result.Error 88 | } 89 | -------------------------------------------------------------------------------- /biz/service/user/user_login_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | user "xzdp/biz/model/user" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/test/assert" 11 | ) 12 | 13 | func TestUserLoginService_Run(t *testing.T) { 14 | ctx := context.Background() 15 | c := app.NewContext(1) 16 | s := NewUserLoginService(ctx, c) 17 | // init req and assert value 18 | req := &user.UserLoginFrom{} 19 | resp, err := s.Run(req) 20 | assert.DeepEqual(t, nil, resp) 21 | assert.DeepEqual(t, nil, err) 22 | // todo edit your unit test. 23 | } 24 | -------------------------------------------------------------------------------- /biz/service/user/user_me.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | user "xzdp/biz/model/user" 7 | "xzdp/biz/utils" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/common/hlog" 11 | ) 12 | 13 | type UserMeService struct { 14 | RequestContext *app.RequestContext 15 | Context context.Context 16 | } 17 | 18 | func NewUserMeService(Context context.Context, RequestContext *app.RequestContext) *UserMeService { 19 | return &UserMeService{RequestContext: RequestContext, Context: Context} 20 | } 21 | 22 | func (h *UserMeService) Run(req *user.Empty) (resp *user.UserDTO, err error) { 23 | defer func() { 24 | hlog.CtxInfof(h.Context, "req = %+v", req) 25 | hlog.CtxInfof(h.Context, "resp = %+v", resp) 26 | }() 27 | // todo edit your code 28 | userdto := utils.GetUser(h.Context) 29 | return userdto, nil 30 | } 31 | -------------------------------------------------------------------------------- /biz/service/user/user_me_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | user "xzdp/biz/model/user" 10 | ) 11 | 12 | func TestUserMeService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewUserMeService(ctx, c) 16 | // init req and assert value 17 | req := &user.Empty{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/user/user_sign.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "xzdp/biz/dal/redis" 10 | user "xzdp/biz/model/user" 11 | "xzdp/biz/pkg/constants" 12 | "xzdp/biz/utils" 13 | 14 | "github.com/cloudwego/hertz/pkg/app" 15 | "github.com/cloudwego/hertz/pkg/common/hlog" 16 | ) 17 | 18 | type UserSignService struct { 19 | RequestContext *app.RequestContext 20 | Context context.Context 21 | } 22 | 23 | func NewUserSignService(Context context.Context, RequestContext *app.RequestContext) *UserSignService { 24 | return &UserSignService{RequestContext: RequestContext, Context: Context} 25 | } 26 | 27 | func (h *UserSignService) Run(req *user.Empty) (resp *bool, err error) { 28 | defer func() { 29 | hlog.CtxInfof(h.Context, "req = %+v", req) 30 | hlog.CtxInfof(h.Context, "resp = %+v", resp) 31 | }() 32 | // todo edit your code 33 | userdto := utils.GetUser(h.Context) 34 | if userdto == nil { 35 | return nil, errors.New("用户未登录") 36 | } 37 | userId := userdto.ID 38 | now := time.Now() 39 | keySuffix := now.Format(":200601") 40 | key := constants.USER_SIGN_KEY + fmt.Sprint(userId) + keySuffix 41 | dayOfMonth := now.Day() 42 | err = redis.RedisClient.SetBit(h.Context, key, int64(dayOfMonth-1), 1).Err() 43 | if err != nil { 44 | return nil, err 45 | } 46 | boolResp := true 47 | return &boolResp, nil 48 | } 49 | -------------------------------------------------------------------------------- /biz/service/user/user_sign_count.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "xzdp/biz/dal/redis" 10 | user "xzdp/biz/model/user" 11 | "xzdp/biz/pkg/constants" 12 | "xzdp/biz/utils" 13 | 14 | "github.com/cloudwego/hertz/pkg/app" 15 | ) 16 | 17 | type UserSignCountService struct { 18 | RequestContext *app.RequestContext 19 | Context context.Context 20 | } 21 | 22 | func NewUserSignCountService(Context context.Context, RequestContext *app.RequestContext) *UserSignCountService { 23 | return &UserSignCountService{RequestContext: RequestContext, Context: Context} 24 | } 25 | 26 | func (h *UserSignCountService) Run(req *user.Empty) (resp *int64, err error) { 27 | //defer func() { 28 | // hlog.CtxInfof(h.Context, "req = %+v", req) 29 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 30 | //}() 31 | // todo edit your code 32 | userdto := utils.GetUser(h.Context) 33 | if userdto == nil { 34 | return nil, errors.New("用户未登录") 35 | } 36 | userId := userdto.ID 37 | now := time.Now() 38 | keySuffix := now.Format(":200601") 39 | key := constants.USER_SIGN_KEY + fmt.Sprint(userId) + keySuffix 40 | dayOfMonth := now.Day() 41 | var signCount int64 = 0 42 | bitFieldResult, err := redis.RedisClient.BitField(h.Context, key, "GET", fmt.Sprintf("u%d", dayOfMonth), "0").Result() 43 | if err != nil { 44 | return nil, err 45 | } 46 | if len(bitFieldResult) == 0 || bitFieldResult[0] == 0 { 47 | return &signCount, nil 48 | } 49 | 50 | num := bitFieldResult[0] 51 | for num > 0 { 52 | if num&1 == 0 { 53 | break 54 | } 55 | signCount++ 56 | num >>= 1 57 | } 58 | 59 | return &signCount, nil 60 | } 61 | -------------------------------------------------------------------------------- /biz/service/user/user_sign_count_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | user "xzdp/biz/model/user" 10 | ) 11 | 12 | func TestUserSignCountService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewUserSignCountService(ctx, c) 16 | // init req and assert value 17 | req := &user.Empty{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/user/user_sign_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | user "xzdp/biz/model/user" 10 | ) 11 | 12 | func TestUserSignService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewUserSignService(ctx, c) 16 | // init req and assert value 17 | req := &user.Empty{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/voucher/seckill_voucher.go: -------------------------------------------------------------------------------- 1 | package voucher 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "sync" 8 | "time" 9 | "xzdp/biz/dal/mysql" 10 | "xzdp/biz/dal/redis" 11 | voucherModel "xzdp/biz/model/voucher" 12 | "xzdp/biz/pkg/constants" 13 | "xzdp/biz/utils" 14 | 15 | "github.com/cloudwego/hertz/pkg/app" 16 | ) 17 | 18 | type SeckillVoucherService struct { 19 | RequestContext *app.RequestContext 20 | Context context.Context 21 | } 22 | 23 | var ( 24 | wg sync.WaitGroup 25 | ) 26 | 27 | func NewSeckillVoucherService(Context context.Context, RequestContext *app.RequestContext) *SeckillVoucherService { 28 | return &SeckillVoucherService{RequestContext: RequestContext, Context: Context} 29 | } 30 | 31 | func (h *SeckillVoucherService) Run(req *int64) (resp *int64, err error) { 32 | //defer func() { 33 | // hlog.CtxInfof(h.Context, "req = %+v", req) 34 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 35 | //}() 36 | // todo edit your code 37 | //0.查询秒杀券 38 | voucher, err := mysql.QueryVoucherByID(h.Context, *req) 39 | if err != nil { 40 | return nil, err 41 | } 42 | fmt.Println(voucher) 43 | now := time.Now() 44 | //1.判断是否开始&&结束 45 | layout := "2006-01-02T15:04:05+08:00" 46 | beginTime, _ := time.Parse(layout, voucher.GetBeginTime()) 47 | endTime, _ := time.Parse(layout, voucher.GetEndTime()) 48 | if beginTime.After(now) { 49 | return nil, errors.New("秒杀还未开始") 50 | } 51 | if endTime.Before(now) { 52 | return nil, errors.New("秒杀已经结束") 53 | } 54 | //2.判断库存是否充足 55 | if voucher.GetStock() <= 0 { 56 | return nil, errors.New("已抢空") 57 | } 58 | user := utils.GetUser(h.Context) 59 | fmt.Println(user) 60 | //uuid, _ := utils.RandomUUID() 61 | //sec := time.Now().Unix() 62 | //lockValue := uuid + strconv.FormatInt(sec, 10) //由于value的全局唯一性,这里用uuid+时间戳,如需要更高精度应考虑雪花算法活其他方法生成 63 | //lock := redis.NewLock(h.Context, user.NickName, lockValue, 10) 64 | //ok := lock.TryLock() 65 | mutex := redis.RedsyncClient.NewMutex(fmt.Sprintf("%s:%s", constants.VOUCHER_LOCK_KEY, user.NickName)) 66 | err = mutex.Lock() 67 | if err != nil { 68 | return nil, errors.New("获取分布式锁失败") 69 | } 70 | // 使用通道来接收 createOrder 的结果 71 | resultCh := make(chan *int64, 1) 72 | errCh := make(chan error, 1) 73 | wg.Add(1) 74 | go func() { 75 | defer wg.Done() 76 | order, err := h.createOrder(*req) 77 | if err != nil { 78 | errCh <- err 79 | } else { 80 | resultCh <- order 81 | } 82 | }() 83 | if ok, err := mutex.Unlock(); !ok || err != nil { 84 | return nil, err 85 | } 86 | select { 87 | case order := <-resultCh: 88 | return order, nil 89 | case err := <-errCh: 90 | return nil, err 91 | case <-h.Context.Done(): 92 | return nil, errors.New("请求超时") 93 | } 94 | } 95 | 96 | func (h *SeckillVoucherService) createOrder(voucherId int64) (resp *int64, err error) { 97 | //3.判断是否已经购买 98 | userId := utils.GetUser(h.Context).GetID() 99 | err = mysql.QueryVoucherOrderByVoucherID(h.Context, userId, voucherId) 100 | if err != nil { 101 | return nil, err 102 | } 103 | //4.扣减库存 104 | err = mysql.UpdateVoucherStock(h.Context, voucherId) 105 | if err != nil { 106 | return nil, err 107 | } 108 | //5.创建订单 109 | orderId, err := redis.NextId(h.Context, "order") 110 | if err != nil { 111 | return nil, err 112 | } 113 | voucherOrder := &voucherModel.VoucherOrder{ 114 | UserId: userId, 115 | VoucherId: voucherId, 116 | OrderId: orderId, 117 | PayTime: time.Now().Format("2006-01-02T15:04:05+08:00"), 118 | UseTime: time.Now().Format("2006-01-02T15:04:05+08:00"), 119 | RefundTime: time.Now().Format("2006-01-02T15:04:05+08:00"), 120 | } 121 | err = mysql.CreateVoucherOrder(h.Context, voucherOrder) 122 | if err != nil { 123 | return nil, err 124 | } 125 | return &orderId, nil 126 | } 127 | -------------------------------------------------------------------------------- /biz/service/voucher/seckill_voucher_test.go: -------------------------------------------------------------------------------- 1 | package voucher 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "xzdp/biz/dal/mysql" 8 | "xzdp/biz/dal/redis" 9 | "xzdp/biz/model/user" 10 | "xzdp/biz/utils" 11 | 12 | "github.com/cloudwego/hertz/pkg/app" 13 | "github.com/cloudwego/hertz/pkg/common/test/assert" 14 | ) 15 | 16 | func setup() { 17 | mysql.Init() 18 | redis.Init() 19 | } 20 | 21 | func TestSeckillVoucherService_Run(t *testing.T) { 22 | setup() 23 | ctx := context.Background() 24 | c := app.NewContext(1) 25 | s := NewSeckillVoucherService(ctx, c) 26 | s.Context = utils.SaveUser(s.Context, &user.UserDTO{ 27 | ID: 6, 28 | NickName: "test", 29 | }) 30 | // init req and assert value 31 | req := int64(1) 32 | resp, err := s.Run(&req) 33 | fmt.Println(resp, err) 34 | assert.DeepEqual(t, nil, resp) 35 | assert.DeepEqual(t, nil, err) 36 | // todo edit your unit test. 37 | } 38 | 39 | func TestSeckillVoucherService_StressTest(t *testing.T) { 40 | setup() 41 | 42 | concurrency := 10 43 | requests := 100 44 | 45 | done := make(chan bool, concurrency) 46 | 47 | for i := 0; i < requests; i++ { 48 | go func(i int) { 49 | defer func() { done <- true }() 50 | ctx := context.Background() 51 | c := app.NewContext(1) 52 | s := NewSeckillVoucherService(ctx, c) 53 | s.Context = utils.SaveUser(s.Context, &user.UserDTO{ 54 | ID: int64(i), 55 | NickName: "test_" + fmt.Sprintf("%d", i), 56 | }) 57 | req := int64(1) 58 | resp, err := s.Run(&req) 59 | if err != nil { 60 | t.Errorf("request %d failed: %v", i, err) 61 | } 62 | if resp != nil { 63 | t.Errorf("request %d got unexpected response: %v", i, resp) 64 | } 65 | }(i) 66 | } 67 | 68 | // 等待所有请求完成 69 | for i := 0; i < requests; i++ { 70 | <-done 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /biz/service/voucher/voucher_list.go: -------------------------------------------------------------------------------- 1 | package voucher 2 | 3 | import ( 4 | "context" 5 | "github.com/cloudwego/hertz/pkg/app" 6 | "xzdp/biz/dal/mysql" 7 | voucher "xzdp/biz/model/voucher" 8 | ) 9 | 10 | type VoucherListService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewVoucherListService(Context context.Context, RequestContext *app.RequestContext) *VoucherListService { 16 | return &VoucherListService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *VoucherListService) Run(id int64) (resp *[]*voucher.SeckillVoucher, err error) { 20 | result, err := mysql.QueryShopVoucherByShopID(h.Context, id) 21 | return &result, nil 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/voucher/voucher_list_test.go: -------------------------------------------------------------------------------- 1 | package voucher 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "xzdp/biz/dal/mysql" 7 | 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/hertz/pkg/common/test/assert" 10 | ) 11 | 12 | func TestVoucherListService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | mysql.Init() 15 | c := app.NewContext(1) 16 | s := NewVoucherListService(ctx, c) 17 | // init req and assert value 18 | resp, err := s.Run(1) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/service/xzdp/hello_method.go: -------------------------------------------------------------------------------- 1 | package xzdp 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/hertz/pkg/app" 7 | xzdp "xzdp/biz/model/xzdp" 8 | ) 9 | 10 | type HelloMethodService struct { 11 | RequestContext *app.RequestContext 12 | Context context.Context 13 | } 14 | 15 | func NewHelloMethodService(Context context.Context, RequestContext *app.RequestContext) *HelloMethodService { 16 | return &HelloMethodService{RequestContext: RequestContext, Context: Context} 17 | } 18 | 19 | func (h *HelloMethodService) Run(req *xzdp.HelloReq) (resp *xzdp.HelloResp, err error) { 20 | //defer func() { 21 | // hlog.CtxInfof(h.Context, "req = %+v", req) 22 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 23 | //}() 24 | // todo edit your code 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /biz/service/xzdp/hello_method_test.go: -------------------------------------------------------------------------------- 1 | package xzdp 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/test/assert" 9 | xzdp "xzdp/biz/model/xzdp" 10 | ) 11 | 12 | func TestHelloMethodService_Run(t *testing.T) { 13 | ctx := context.Background() 14 | c := app.NewContext(1) 15 | s := NewHelloMethodService(ctx, c) 16 | // init req and assert value 17 | req := &xzdp.HelloReq{} 18 | resp, err := s.Run(req) 19 | assert.DeepEqual(t, nil, resp) 20 | assert.DeepEqual(t, nil, err) 21 | // todo edit your unit test. 22 | } 23 | -------------------------------------------------------------------------------- /biz/utils/directory.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func PathExists(path string) (bool, error) { 8 | _, err := os.Stat(path) 9 | if err == nil { 10 | return true, nil 11 | } 12 | if os.IsNotExist(err) { 13 | return false, nil 14 | } 15 | return false, err 16 | } 17 | 18 | func CreateDir(dirs ...string) (err error) { 19 | for _, v := range dirs { 20 | exist, err := PathExists(v) 21 | if err != nil { 22 | return err 23 | } 24 | if !exist { 25 | if err := os.MkdirAll(v, os.ModePerm); err != nil { 26 | return err 27 | } 28 | } 29 | } 30 | return err 31 | } 32 | -------------------------------------------------------------------------------- /biz/utils/error.go: -------------------------------------------------------------------------------- 1 | // pkg/utils/errors.go 2 | package utils 3 | 4 | import "errors" 5 | 6 | var ( 7 | ErrInvalidParameter = errors.New("invalid parameter") 8 | ErrNotFound = errors.New("not found") 9 | ) 10 | -------------------------------------------------------------------------------- /biz/utils/random.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var ( 11 | once sync.Once 12 | ) 13 | 14 | const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 15 | const digitBytes = "0123456789" 16 | 17 | func RandInit() { 18 | rand.New(rand.NewSource(time.Now().UnixNano())) // fix the deprecated method rand.Seed(time.Now().UnixNano()) 19 | } 20 | 21 | func GenerateDigits(length int) string { 22 | once.Do(RandInit) 23 | b := make([]byte, length) 24 | for i := range b { 25 | b[i] = digitBytes[rand.Intn(len(digitBytes))] 26 | } 27 | return string(b) 28 | } 29 | 30 | func GenerateLetters(length int) string { 31 | once.Do(RandInit) 32 | b := make([]byte, length) 33 | for i := range b { 34 | b[i] = letterBytes[rand.Intn(len(letterBytes))] 35 | } 36 | return string(b) 37 | } 38 | 39 | func GenerateAlphaNumeric(length int) string { 40 | once.Do(RandInit) 41 | b := make([]byte, length) 42 | combinedBytes := letterBytes + digitBytes 43 | for i := range b { 44 | b[i] = combinedBytes[rand.Intn(len(combinedBytes))] 45 | } 46 | return string(b) 47 | } 48 | 49 | func RandomUUID() (string, error) { 50 | 51 | randomBytes := make([]byte, 16) 52 | combinedBytes := letterBytes + digitBytes 53 | for i := range randomBytes { 54 | randomBytes[i] = combinedBytes[rand.Intn(len(combinedBytes))] 55 | } 56 | 57 | randomBytes[6] &= 0x0f // clear version 58 | randomBytes[6] |= 0x40 // set version to 4 (random uuid) 59 | randomBytes[8] &= 0x3f // clear variant 60 | randomBytes[8] |= 0x80 // set to IETF variant 61 | return fmt.Sprintf("%x", randomBytes), nil 62 | 63 | // return fmt.Sprintf("%x-%x-%x-%x-%x", randomBytes[0:4], randomBytes[4:6], randomBytes[6:8], randomBytes[8:10], randomBytes[10:]), nil 64 | } 65 | 66 | func RandomString(n int) string { 67 | once.Do(RandInit) 68 | letters := letterBytes + digitBytes 69 | b := make([]byte, n) 70 | for i := range b { 71 | b[i] = letters[rand.Intn(len(letters))] 72 | } 73 | return string(b) 74 | } 75 | -------------------------------------------------------------------------------- /biz/utils/resp.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "xzdp/biz/model/xzdp" 6 | 7 | "github.com/cloudwego/hertz/pkg/app" 8 | ) 9 | 10 | // SendErrResponse pack error response 11 | func SendErrResponse(ctx context.Context, c *app.RequestContext, code int, err error) { 12 | // todo edit custom code 13 | response := xzdp.NewFailureResponse(err.Error()) 14 | c.JSON(code, response) 15 | } 16 | 17 | // SendSuccessResponse pack success response 18 | func SendSuccessResponse(ctx context.Context, c *app.RequestContext, code int, data interface{}) { 19 | // todo edit custom code 20 | response := xzdp.NewSuccessResponse(data) 21 | c.JSON(code, response) 22 | } 23 | 24 | func SendRawResponse(ctx context.Context, c *app.RequestContext, code int, data interface{}) { 25 | // todo edit custom code 26 | c.JSON(code, data) 27 | } 28 | -------------------------------------------------------------------------------- /biz/utils/string_utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | func ValidateMobile(phone string) bool { 8 | var re = regexp.MustCompile(`^1[3-9]\d{9}$`) 9 | return re.MatchString(phone) 10 | } 11 | -------------------------------------------------------------------------------- /biz/utils/struct.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "reflect" 7 | ) 8 | 9 | func MapToStructByJson(m map[string]interface{}, s interface{}) error { 10 | targetValue := reflect.ValueOf(s) 11 | if targetValue.Kind() != reflect.Ptr || targetValue.IsNil() { 12 | return errors.New("target must be a non-nil pointer") 13 | } 14 | jsonBytes, err := json.Marshal(m) 15 | if err != nil { 16 | return err 17 | } 18 | err = json.Unmarshal(jsonBytes, s) 19 | return nil 20 | } 21 | 22 | func SerializeStruct(s interface{}) (string, error) { 23 | targetValue := reflect.ValueOf(s) 24 | if targetValue.Kind() != reflect.Ptr || targetValue.IsNil() { 25 | return "", errors.New("target must be a non-nil pointer") 26 | } 27 | // 使用json.Marshal将response对象序列化为字节切片 28 | respBytes, err := json.Marshal(s) 29 | if err != nil { 30 | return "", err 31 | } 32 | 33 | return string(respBytes), nil 34 | } 35 | 36 | func UnSerializeStruct(jsonStr string, s interface{}) error { 37 | jsonBytes := []byte(jsonStr) 38 | targetValue := reflect.ValueOf(s) 39 | if targetValue.Kind() != reflect.Ptr || targetValue.IsNil() { 40 | return errors.New("target must be a non-nil pointer") 41 | } 42 | if err := json.Unmarshal(jsonBytes, s); err != nil { 43 | return err 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /biz/utils/user.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "xzdp/biz/model/user" 4 | 5 | func UserToUserDTO(u *user.User) *user.UserDTO { 6 | return &user.UserDTO{ 7 | ID: u.ID, 8 | NickName: u.NickName, 9 | Icon: u.Icon, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /biz/utils/user_holder.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | model "xzdp/biz/model/user" 6 | ) 7 | 8 | func SaveUser(ctx context.Context, user *model.UserDTO) context.Context { 9 | return context.WithValue(ctx, "user", user) 10 | } 11 | 12 | // GetUser retrieves the user information from the context. 13 | func GetUser(ctx context.Context) *model.UserDTO { 14 | user, ok := ctx.Value("user").(*model.UserDTO) 15 | if !ok { 16 | return nil 17 | } 18 | return user 19 | } 20 | 21 | // RemoveUser creates a new context without the user information. 22 | func RemoveUser(ctx context.Context) context.Context { 23 | return context.WithValue(ctx, "user", nil) 24 | } 25 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | RUN_NAME=hertz_service 3 | mkdir -p output/bin 4 | cp script/* output 2>/dev/null 5 | chmod +x output/bootstrap.sh 6 | go build -o output/bin/${RUN_NAME} -------------------------------------------------------------------------------- /conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | "sync" 10 | 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "gopkg.in/validator.v2" 13 | "gopkg.in/yaml.v2" 14 | ) 15 | 16 | var ( 17 | conf *Config 18 | once sync.Once 19 | ) 20 | 21 | type Config struct { 22 | Env string 23 | 24 | Hertz Hertz `yaml:"hertz"` 25 | MySQL MySQL `yaml:"mysql"` 26 | Redis Redis `yaml:"redis"` 27 | Cors Cors `yaml:"cors"` 28 | } 29 | 30 | type MySQL struct { 31 | DSN string `yaml:"dsn"` 32 | } 33 | 34 | type Redis struct { 35 | Address string `yaml:"address"` 36 | Password string `yaml:"password"` 37 | } 38 | 39 | type Hertz struct { 40 | Address string `yaml:"address"` 41 | EnablePprof bool `yaml:"enable_pprof"` 42 | EnableGzip bool `yaml:"enable_gzip"` 43 | EnableAccessLog bool `yaml:"enable_access_log"` 44 | LogLevel string `yaml:"log_level"` 45 | LogFileName string `yaml:"log_file_name"` 46 | LogMaxSize int `yaml:"log_max_size"` 47 | LogMaxBackups int `yaml:"log_max_backups"` 48 | LogMaxAge int `yaml:"log_max_age"` 49 | } 50 | type CORSWhitelist struct { 51 | AllowOrigin string `yaml:"allow-origin"` 52 | AllowMethods string `yaml:"allow-methods"` 53 | AllowHeaders string `yaml:"allow-headers"` 54 | ExposeHeaders string `yaml:"expose-headers"` 55 | AllowCredentials bool `yaml:"allow-credentials"` 56 | } 57 | 58 | type Cors struct { 59 | Mode string `yaml:"mode"` 60 | WhiteList []CORSWhitelist `yaml:"white_list"` 61 | } 62 | 63 | // GetConf gets configuration instance 64 | func GetConf() *Config { 65 | once.Do(initConf) 66 | return conf 67 | } 68 | 69 | func initConf() { 70 | _, confpath, _, ok := runtime.Caller(0) 71 | if !ok { 72 | panic(errors.New("error")) 73 | } 74 | prefix := confpath[0 : len(confpath)-7] 75 | confFileRelPath := filepath.Join(prefix, filepath.Join(GetEnv(), "conf.yaml")) 76 | fmt.Println(confFileRelPath) 77 | content, err := os.ReadFile(confFileRelPath) // replace the deprecated ioutil.ReadFile to os.ReadFile 78 | if err != nil { 79 | panic(err) 80 | } 81 | 82 | conf = new(Config) 83 | err = yaml.Unmarshal(content, conf) 84 | if err != nil { 85 | hlog.Error("parse yaml error - %v", err) 86 | panic(err) 87 | } 88 | if err := validator.Validate(conf); err != nil { 89 | hlog.Error("validate config error - %v", err) 90 | panic(err) 91 | } 92 | 93 | conf.Env = GetEnv() 94 | 95 | //pretty.Printf("%+v\n", conf) 96 | } 97 | 98 | func GetEnv() string { 99 | e := os.Getenv("GO_ENV") 100 | if len(e) == 0 { 101 | return "test" 102 | } 103 | return e 104 | } 105 | 106 | func LogLevel() hlog.Level { 107 | level := GetConf().Hertz.LogLevel 108 | switch level { 109 | case "trace": 110 | return hlog.LevelTrace 111 | case "debug": 112 | return hlog.LevelDebug 113 | case "info": 114 | return hlog.LevelInfo 115 | case "notice": 116 | return hlog.LevelNotice 117 | case "warn": 118 | return hlog.LevelWarn 119 | case "error": 120 | return hlog.LevelError 121 | case "fatal": 122 | return hlog.LevelFatal 123 | default: 124 | return hlog.LevelInfo 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /conf/dev/conf.example.yaml: -------------------------------------------------------------------------------- 1 | hertz: 2 | address: ":8081" 3 | enable_pprof: true 4 | enable_gzip: true 5 | enable_access_log: true 6 | log_level: info 7 | log_file_name: "log/hertz.log" 8 | log_max_size: 10 9 | log_max_age: 3 10 | log_max_backups: 50 11 | 12 | mysql: 13 | dsn: "root:password@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local" 14 | 15 | redis: 16 | address: "127.0.0.1:6379" 17 | password: "" 18 | 19 | cors: 20 | mode: "allow-all" # allow-all, strict-whitelist 21 | whitelist: 22 | - allow-origin: example1.com 23 | allow-headers: content-type 24 | allow-methods: GET, POST 25 | expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type 26 | allow-credentials: true # bool 27 | - allow-origin: example2.com 28 | allow-headers: content-type 29 | allow-methods: GET, POST 30 | expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type 31 | allow-credentials: true # bool 32 | -------------------------------------------------------------------------------- /conf/online/conf.example.yaml: -------------------------------------------------------------------------------- 1 | hertz: 2 | address: ":8080" 3 | enable_pprof: true 4 | enable_gzip: true 5 | enable_access_log: true 6 | log_level: info 7 | log_file_name: "log/hertz.log" 8 | log_max_size: 10 9 | log_max_age: 3 10 | log_max_backups: 50 11 | 12 | mysql: 13 | dsn: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local" 14 | 15 | redis: 16 | address: "127.0.0.1:6379" 17 | password: "" 18 | 19 | cors: 20 | mode: "allow-all" # allow-all, strict-whitelist 21 | whitelist: 22 | - allow-origin: example1.com 23 | allow-headers: content-type 24 | allow-methods: GET, POST 25 | expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type 26 | allow-credentials: true # bool 27 | - allow-origin: example2.com 28 | allow-headers: content-type 29 | allow-methods: GET, POST 30 | expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type 31 | allow-credentials: true # bool 32 | -------------------------------------------------------------------------------- /conf/test/conf.example.yaml: -------------------------------------------------------------------------------- 1 | hertz: 2 | address: ":8081" 3 | enable_pprof: true 4 | enable_gzip: true 5 | enable_access_log: true 6 | log_level: info 7 | log_file_name: "log/hertz.log" 8 | log_max_size: 10 9 | log_max_age: 3 10 | log_max_backups: 50 11 | 12 | mysql: 13 | dsn: "root:password@tcp(127.0.0.1:3306)/xzdp?charset=utf8&parseTime=True&loc=Local" 14 | 15 | redis: 16 | address: "127.0.0.1:6379" 17 | password: "" 18 | 19 | cors: 20 | mode: "allow-all" # allow-all, strict-whitelist 21 | whitelist: 22 | - allow-origin: example1.com 23 | allow-headers: content-type 24 | allow-methods: GET, POST 25 | expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type 26 | allow-credentials: true # bool 27 | - allow-origin: example2.com 28 | allow-headers: content-type 29 | allow-methods: GET, POST 30 | expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type 31 | allow-credentials: true # bool 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module xzdp 2 | 3 | go 1.22.2 4 | 5 | replace github.com/apache/thrift => github.com/apache/thrift v0.13.0 6 | 7 | require ( 8 | github.com/apache/thrift v0.0.0-00010101000000-000000000000 9 | github.com/cloudwego/hertz v0.9.1 10 | github.com/go-redis/redis/v8 v8.11.5 11 | github.com/go-redsync/redsync/v4 v4.13.0 12 | github.com/hertz-contrib/sse v0.0.5 13 | github.com/jinzhu/copier v0.4.0 14 | github.com/stretchr/testify v1.8.1 15 | gopkg.in/validator.v2 v2.0.1 16 | gopkg.in/yaml.v2 v2.4.0 17 | gorm.io/driver/mysql v1.5.7 18 | gorm.io/gorm v1.25.11 19 | ) 20 | 21 | require ( 22 | github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect 23 | github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect 24 | github.com/alicebob/miniredis/v2 v2.33.0 // indirect 25 | github.com/bytedance/go-tagexpr/v2 v2.9.2 // indirect 26 | github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3 // indirect 27 | github.com/bytedance/sonic v1.11.9 // indirect 28 | github.com/bytedance/sonic/loader v0.1.1 // indirect 29 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 30 | github.com/cloudwego/base64x v0.1.4 // indirect 31 | github.com/cloudwego/iasm v0.2.0 // indirect 32 | github.com/cloudwego/netpoll v0.6.2 // indirect 33 | github.com/davecgh/go-spew v1.1.1 // indirect 34 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 35 | github.com/fsnotify/fsnotify v1.5.4 // indirect 36 | github.com/go-sql-driver/mysql v1.7.0 // indirect 37 | github.com/golang/mock v1.6.0 // indirect 38 | github.com/golang/protobuf v1.5.0 // indirect 39 | github.com/hashicorp/errwrap v1.1.0 // indirect 40 | github.com/hashicorp/go-multierror v1.1.1 // indirect 41 | github.com/henrylee2cn/ameda v1.4.10 // indirect 42 | github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect 43 | github.com/jinzhu/inflection v1.0.0 // indirect 44 | github.com/jinzhu/now v1.1.5 // indirect 45 | github.com/klauspost/cpuid/v2 v2.0.9 // indirect 46 | github.com/kr/pretty v0.3.1 // indirect 47 | github.com/nyaruka/phonenumbers v1.0.55 // indirect 48 | github.com/pmezard/go-difflib v1.0.0 // indirect 49 | github.com/stretchr/objx v0.5.0 // indirect 50 | github.com/tidwall/gjson v1.14.4 // indirect 51 | github.com/tidwall/match v1.1.1 // indirect 52 | github.com/tidwall/pretty v1.2.0 // indirect 53 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 54 | github.com/yuin/gopher-lua v1.1.1 // indirect 55 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect 56 | golang.org/x/mod v0.22.0 // indirect 57 | golang.org/x/sync v0.10.0 // indirect 58 | golang.org/x/sys v0.29.0 // indirect 59 | golang.org/x/text v0.14.0 // indirect 60 | golang.org/x/tools v0.29.0 // indirect 61 | google.golang.org/protobuf v1.28.1 // indirect 62 | gopkg.in/yaml.v3 v3.0.1 // indirect 63 | ) 64 | -------------------------------------------------------------------------------- /idl/blog.thrift: -------------------------------------------------------------------------------- 1 | // idl/blog.thrift 2 | namespace go blog 3 | include "./user.thrift" 4 | struct Empty {} 5 | 6 | 7 | struct Blog { 8 | 1: i64 id (go.tag='gorm:"id"'); 9 | 2: i64 shopId (go.tag='gorm:"shop_id"') 10 | 3: i64 userId (go.tag='gorm:"user_id"') 11 | 4: string title (go.tag='gorm:"title"') 12 | 5: string images (go.tag='gorm:"images"') 13 | 6: string content (go.tag='gorm:"content"') 14 | 7: i64 liked (go.tag='gorm:"liked"') 15 | 8: i64 comments (go.tag='gorm:"comments"') 16 | 9: string createTime (go.tag='gorm:"create_time"'); 17 | 10: string updateTime (go.tag='gorm:"update_time"'); 18 | 11: string icon (go.tag='gorm:"-"'); 19 | 12: string nickName (go.tag='gorm:"-"'); 20 | 13: bool isLiked (go.tag='gorm:"-"'); 21 | } 22 | 23 | struct BlogReq { 24 | 1: i64 current (api.query="current"); 25 | } 26 | 27 | struct LikeResp { 28 | 1: bool isLiked; 29 | } 30 | struct FollowBlogReq { 31 | 1: string lastId (api.query="lastId"); 32 | 2: i64 offset (api.query="offset"); 33 | } 34 | struct FollowBlogRresp { 35 | 1: list list; 36 | 2: string minTime (api.query="minTime"); 37 | 3: i64 offset (api.query="offset"); 38 | } 39 | service BlogService { 40 | list GetHotBlog(1: BlogReq request) (api.get="/blog/hot"); 41 | list GetUserBlog(1: BlogReq request) (api.get="/blog/user/:id"); 42 | list BlogOfMe(1: BlogReq request) (api.get="/blog/of/me"); 43 | // 发布博客 44 | Blog PostBlog(1: Blog request) (api.post="/blog"); 45 | // 查看博客 46 | Blog GetBlog(1: string request) (api.get="/blog/:id"); 47 | // 删除博客 48 | Empty DeleteBlog(1: string request) (api.delete="/blog/:id"); 49 | // 点赞博客 50 | LikeResp LikeBlog(1: string request) (api.put="/blog/like/:id"); 51 | // 点赞用户排行 52 | list GetLikes(1: string request) (api.get="/blog/likes/:id"); 53 | // 关注的博客 54 | FollowBlogRresp GetFollowBlog(1: FollowBlogReq request) (api.get="/blog/of/follow"); 55 | } 56 | -------------------------------------------------------------------------------- /idl/blog_comment.thrift: -------------------------------------------------------------------------------- 1 | // idl/blog_comment.thrift 2 | namespace go blog_comment 3 | struct Empty {} 4 | 5 | 6 | struct BlogComment { 7 | 1: i64 id (go.tag='gorm:"id"'); 8 | 2: i64 blogId (go.tag='gorm:"blog_id"') 9 | 3: i64 userId (go.tag='gorm:"user_id"') 10 | 4: i64 parentId (go.tag='gorm:"parent_id"') 11 | 5: i64 answerId (go.tag='gorm:"answer_id"') 12 | 6: string content (go.tag='gorm:"content"') 13 | 7: i64 liked (go.tag='gorm:"liked"') 14 | 8: i64 comments (go.tag='gorm:"comments"') 15 | 9: string createTime (go.tag='gorm:"create_time"'); 16 | 10: string updateTime (go.tag='gorm:"update_time"'); 17 | } 18 | 19 | struct CommentReq { 20 | 1: i64 current (api.query="current"); 21 | } 22 | struct LikeResp { 23 | 1: bool isLiked; 24 | } 25 | service CommentService { 26 | // 获取热评 27 | list GetHotComment(1: CommentReq request) (api.get="/comment/hot"); 28 | // 获取评论 29 | list GetComment(1: CommentReq request) (api.get="/comment/:blogID"); 30 | // 发布评论 31 | BlogComment PostComment(1: BlogComment request) (api.post="/comment/post"); 32 | // 点赞评论 33 | LikeResp LikeComment(1: string request) (api.put="/comment/like/:id"); 34 | // 删除评论 35 | Empty DeleteComment(1: string request) (api.delete="/comment/:id"); 36 | } 37 | -------------------------------------------------------------------------------- /idl/follow.thrift: -------------------------------------------------------------------------------- 1 | // idl/follow.thrift 2 | namespace go follow 3 | include "./user.thrift" 4 | 5 | 6 | struct Follow { 7 | 1: i64 id(go.tag='gorm:"id"') 8 | 2: i64 userId(go.tag='gorm:"user_id"') 9 | 3: i64 followUserId(go.tag='gorm:"follow_user_id"') 10 | 4: string createTime (go.tag='gorm:"create_time default:CURRENT_TIMESTAMP"') 11 | } 12 | 13 | struct FollowReq { 14 | 1: bool isFollow 15 | 2: i64 targetUser 16 | } 17 | 18 | struct FollowResp { 19 | 1: Follow RespBody; 20 | } 21 | 22 | struct isFollowedResp{ 23 | 1: bool isFollowed; 24 | } 25 | struct commonFollowReq{ 26 | 1: i64 userId(api.query="userId"); 27 | } 28 | struct commonFollowResp{ 29 | 1: list commonFollows; 30 | } 31 | service FollowService { 32 | FollowResp Follow(1: FollowReq request) (api.put="/follow/:id/:isFollow"); 33 | isFollowedResp isFollowed(1: string request) (api.get="/follow/or/not/:id"); 34 | commonFollowResp commonFollow(1: string request) (api.get="/follow/common/:id"); 35 | } 36 | -------------------------------------------------------------------------------- /idl/image.thrift: -------------------------------------------------------------------------------- 1 | // idl/image.thrift 2 | namespace go image 3 | struct Empty {} 4 | struct UploadResp { 5 | 1: string url; 6 | } 7 | service ImageService { 8 | UploadResp Upload(1: binary request) (api.post="/upload/blog"); 9 | } 10 | -------------------------------------------------------------------------------- /idl/message.thrift: -------------------------------------------------------------------------------- 1 | // idl/message.thrift 2 | namespace go message 3 | include "./user.thrift" 4 | struct Empty {} 5 | struct Message { 6 | 1: i64 from (go.tag='gorm:"-"'); 7 | 2: i64 to (go.tag='gorm:"-"'); 8 | 3: string content (go.tag='gorm:"-"'); 9 | 4: string type (go.tag='gorm:"-"'); 10 | 5: string time (go.tag='gorm:"-"'); 11 | } 12 | struct MessageResp { 13 | 1: user.UserDTO user; 14 | 2: Message message; 15 | } 16 | service MessageService { 17 | string Sse(1: string request) (api.get="/message/sse"); 18 | } 19 | -------------------------------------------------------------------------------- /idl/shop.thrift: -------------------------------------------------------------------------------- 1 | // idl/shop.thrift 2 | namespace go shop 3 | 4 | // import "github.com/hertz-contrib/sessions" 5 | 6 | struct Empty {} 7 | 8 | struct ShopType { 9 | 1: i64 id 10 | 2: string name 11 | 3: string icon 12 | 4: i32 sort 13 | } 14 | 15 | struct ShopOfTypeReq { 16 | 1: i32 typeId (api.query="typeId"); 17 | 2: i64 current (api.query="current"); 18 | } 19 | 20 | struct ShopOfTypeGeoReq { 21 | 1: i32 typeId (api.query="typeId"); 22 | 2: i64 current (api.query="current"); 23 | 3: double longitude (api.query="x"); 24 | 4: double latitude (api.query="y"); 25 | 5: double distance (api.query="dist"); 26 | } 27 | 28 | struct Shop { 29 | 1: i64 id (go.tag='gorm:"id",redis:"id"'); 30 | 2: string name (go.tag='gorm:"name",redis:"name"'); 31 | 3: i64 typeId (go.tag='gorm:"type_id",redis:"typeId"'); 32 | 4: string images (go.tag='gorm:"images",redis:"images"'); 33 | 5: string area (go.tag='gorm:"area",redis:"area"'); 34 | 6: string address (go.tag='gorm:"address",redis:"address"'); 35 | 7: double x (go.tag='gorm:"longitude",redis:"longitude"'); 36 | 8: double y (go.tag='gorm:"latitude",redis:"latitude"'); 37 | 9: i64 avgPrice (go.tag='gorm:"avg_price",redis:"avgPrice"'); 38 | 10: i32 sold (go.tag='gorm:"sold",redis:"sold"'); 39 | 11: i32 comments (go.tag='gorm:"comments",redis:"comments"'); 40 | 12: i32 score (go.tag='gorm:"score",redis:"score"'); 41 | 13: string openHours (go.tag='gorm:"open_hours",redis:"openHours"'); 42 | 14: string createTime (go.tag='gorm:"create_time",redis:"createTime"'); 43 | 15: string updateTime (go.tag='gorm:"update_time",redis:"updateTime"'); 44 | 16: double distance (go.tag='gorm:"-",redis:"-"'); 45 | } 46 | service ShopService { 47 | list ShopList(1: Empty request) (api.get="/shop-type/list"); 48 | list ShopOfType(1: ShopOfTypeReq request) (api.get="/shop/of/type"); 49 | list ShopOfTypeGeo(1: ShopOfTypeGeoReq request) (api.get="/shop/of/type/geo"); 50 | Shop ShopInfo(1: Empty request) (api.get="/shop/:id"); 51 | } 52 | -------------------------------------------------------------------------------- /idl/user.thrift: -------------------------------------------------------------------------------- 1 | // idl/user.thrift 2 | namespace go user 3 | 4 | struct Empty {} 5 | 6 | struct Session { 7 | 1: string Token; 8 | } 9 | struct UserLoginFrom { 10 | 1: string Phone (api.query="phone" go.tag='gorm:"phone"'); 11 | 2: string code (api.query="code"); 12 | 3: string password (api.query="password"); 13 | 4: Session session (api.query="session"); 14 | } 15 | 16 | struct User { 17 | 1: string Phone (go.tag='gorm:"phone"'); 18 | 2: string code (go.tag='gorm:"-"'); 19 | 3: string password (go.tag='gorm:"password"'); 20 | 4: i64 id (go.tag='gorm:"id"'); 21 | 5: string NickName (go.tag='gorm:"nick_name"'); 22 | 6: string icon (go.tag='gorm:"icon"'); 23 | 7: string createTime (go.tag='gorm:"create_time"'); 24 | 8: string updateTime (go.tag='gorm:"update_time"'); 25 | } 26 | 27 | struct Result { 28 | 1: bool success, 29 | 2: optional string errorMsg, 30 | 3: optional string data, // 使用 string 类型来表示泛型对象。你可以根据需要选择合适的数据类型。 31 | 4: optional i64 total, 32 | } 33 | 34 | struct UserResp { 35 | 1: User RespBody; 36 | } 37 | 38 | struct UserDTO { 39 | 1: i64 id (go.tag = 'redis:"id"'), 40 | 2: string NickName (go.tag = 'redis:"nick_name" form:"nickName" json:"nickName" query:"nickName"') 41 | 3: string icon (go.tag = 'redis:"icon"') 42 | } 43 | 44 | struct UserInfo { 45 | 1: i64 userId (go.tag='gorm:"user_id"'); 46 | 2: string city (go.tag='gorm:"city"'); 47 | 3: string introduce (go.tag='gorm:"introduce"'); 48 | 4: i64 fans (go.tag='gorm:"fans"'); 49 | 5: i64 followee (go.tag='gorm:"followee"'); 50 | 6: i64 gender (go.tag='gorm:"gender"'); 51 | 7: string birthday (go.tag='gorm:"birthday"'); 52 | 8: i64 credits (go.tag='gorm:"credits"'); 53 | 9: i64 level (go.tag='gorm:"level"'); 54 | 10: string createTime (go.tag='gorm:"create_time"'); 55 | 11: string updateTime (go.tag='gorm:"update_time"'); 56 | } 57 | 58 | service UserService { 59 | UserDTO UserMe(1: Empty request) (api.get="/user/me"); 60 | UserResp SendCode(1: UserLoginFrom request) (api.post="/user/code"); 61 | UserResp UserLogin(1: UserLoginFrom request) (api.post="/user/login"); 62 | UserInfo UserInfo(1: UserLoginFrom request) (api.get="/user/info/:id"); 63 | bool UserSign(1: Empty request) (api.post="/user/sign"); 64 | i64 UserSignCount(1: Empty request) (api.get="/user/sign/count"); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /idl/voucher.thrift: -------------------------------------------------------------------------------- 1 | namespace go voucher 2 | 3 | struct Empty {} 4 | 5 | struct Voucher { 6 | 1: i64 id (go.tag='gorm:"id"'); 7 | 2: i64 shopId (go.tag='gorm:"shop_id"'); 8 | 3: string title (go.tag='gorm:"title"'); 9 | 4: string subTitle (go.tag='gorm:"sub_title"'); 10 | 5: string rules (go.tag='gorm:"rules"'); 11 | 6: i64 payValue (go.tag='gorm:"pay_value"'); 12 | 7: i64 actualValue (go.tag='gorm:"actual_value"'); 13 | 8: i8 type (go.tag='gorm:"type"'); 14 | 9: i8 status (go.tag='gorm:"status"'); 15 | 10: string createTime (go.tag='gorm:"create_time"'); 16 | 11: string updateTime (go.tag='gorm:"update_time"'); 17 | } 18 | 19 | struct SeckillVoucher { 20 | 1: i64 id (go.tag='gorm:"id"'); 21 | 2: i64 shopId (go.tag='gorm:"shop_id"'); 22 | 3: string title (go.tag='gorm:"title"'); 23 | 4: string subTitle (go.tag='gorm:"sub_title"'); 24 | 5: string rules (go.tag='gorm:"rules"'); 25 | 6: i64 payValue (go.tag='gorm:"pay_value"'); 26 | 7: i64 actualValue (go.tag='gorm:"actual_value"'); 27 | 8: i8 type (go.tag='gorm:"type"'); 28 | 9: i8 status (go.tag='gorm:"status"'); 29 | 10: string createTime (go.tag='gorm:"create_time"'); 30 | 11: string updateTime (go.tag='gorm:"update_time"'); 31 | 12: i64 voucherId (go.tag='gorm:"voucher_id"'); 32 | 13: i32 stock (go.tag='gorm:"stock"'); 33 | 15: string beginTime (go.tag='gorm:"begin_time"'); 34 | 16: string endTime (go.tag='gorm:"end_time"'); 35 | } 36 | 37 | struct VoucherOrder { 38 | 1: i64 id (go.tag='gorm:"column:id;primaryKey;autoIncrement:false"'); 39 | 2: i64 userId (go.tag='gorm:"column:user_id"'); 40 | 3: i64 voucherId (go.tag='gorm:"column:voucher_id"'); 41 | 4: i32 payType (go.tag='gorm:"column:pay_type;default:1"'); 42 | 5: i32 status (go.tag='gorm:"column:status;default:1"'); 43 | 6: string createTime (go.tag='gorm:"column:create_time;default:CURRENT_TIMESTAMP"'); 44 | 7: string payTime (go.tag='gorm:"column:pay_time"'); 45 | 8: string useTime (go.tag='gorm:"column:use_time"'); 46 | 9: string refundTime (go.tag='gorm:"column:refund_time"'); 47 | 10: string updateTime (go.tag='gorm:"column:update_time;default:CURRENT_TIMESTAMP;autoUpdateTime"'); 48 | 11: i64 orderId (go.tag='gorm:"column:order_id"'); 49 | } 50 | 51 | service VoucherService { 52 | list VoucherList(1: i64 request) (api.get="/voucher/list/:id"); 53 | i64 SeckillVoucher(1: i64 request) (api.post="/voucher-order/seckill/:id"); 54 | } 55 | -------------------------------------------------------------------------------- /idl/xzdp.thrift: -------------------------------------------------------------------------------- 1 | // idl/xzdp.thrift 2 | namespace go xzdp 3 | 4 | struct HelloReq { 5 | 1: string Name (api.query="name"); 6 | } 7 | 8 | struct HelloResp { 9 | 1: string RespBody; 10 | } 11 | 12 | service HelloService { 13 | HelloResp HelloMethod(1: HelloReq request) (api.get="/hello"); 14 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "xzdp/biz/dal/mysql" 8 | "xzdp/biz/dal/redis" 9 | "xzdp/biz/middleware/interceptor" 10 | "xzdp/conf" 11 | 12 | "net/http" 13 | _ "net/http/pprof" // 导入 pprof HTTP handler 14 | 15 | "github.com/cloudwego/hertz/pkg/app" 16 | "github.com/cloudwego/hertz/pkg/app/server" 17 | "github.com/cloudwego/hertz/pkg/common/hlog" 18 | ) 19 | 20 | func main() { 21 | go func() { 22 | _ = http.ListenAndServe(":6060", nil) 23 | }() 24 | h := server.Default(server.WithHostPorts(conf.GetConf().Hertz.Address)) 25 | h.Use(interceptor.CheckToken) 26 | h.Use(interceptor.Cors) 27 | excludedPaths := []string{ 28 | "/shop", 29 | "/voucher", 30 | "/shop-type", 31 | "/upload", 32 | "/blog/hot", 33 | "/user/code", 34 | "/user/login", 35 | "/follow", 36 | "/imgs", 37 | "/message", 38 | "/blog", 39 | } 40 | 41 | // excludedPaths := map[string]bool{ 42 | // "/shop": true, 43 | // "/voucher": true, 44 | // "/shop-type/list": true, 45 | // "/upload": true, 46 | // "/blog/hot": true, 47 | // "/user/code": true, 48 | // "/user/login": true, 49 | // } 50 | h.Use(func(ctx context.Context, c *app.RequestContext) { 51 | path := string(c.Request.Path()) 52 | for i := 0; i < len(excludedPaths); i++ { 53 | if len(path) < len(excludedPaths[i]) { 54 | continue 55 | } 56 | if path[:len(excludedPaths[i])] == excludedPaths[i] { 57 | return 58 | } 59 | } 60 | hlog.CtxInfof(ctx, "path = %s", path) 61 | interceptor.LoginInterceptor(ctx, c) 62 | }) 63 | 64 | mysql.Init() 65 | redis.Init() 66 | h.Use(interceptor.UniqueVisitor) 67 | 68 | register(h) 69 | h.Spin() 70 | } 71 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | init_api: 2 | hz new --mod=xzdp --idl=idl/xzdp.thrift --customize_package=template/init.yaml --customize_layout_data_path=template/layout.yaml 3 | 4 | update_api: 5 | hz update --mod=xzdp --idl=idl/xzdp.thrift --customize_package=template/package.yaml 6 | hz update --mod=xzdp --idl=idl/blog.thrift --customize_package=template/package.yaml 7 | hz update --mod=xzdp --idl=idl/user.thrift --customize_package=template/package.yaml 8 | hz update --mod=xzdp --idl=idl/shop.thrift --customize_package=template/package.yaml 9 | hz update --mod=xzdp --idl=idl/follow.thrift --customize_package=template/package.yaml 10 | hz update --mod=xzdp --idl=idl/image.thrift --customize_package=template/package.yaml 11 | hz update --mod=xzdp --idl=idl/blog_comment.thrift --customize_package=template/package.yaml 12 | hz update --mod=xzdp --idl=idl/message.thrift --customize_package=template/package.yaml 13 | hz update --mod=xzdp --idl=idl/voucher.thrift --customize_package=template/package.yaml 14 | 15 | cleanhz: 16 | rm -rf biz *.go go.sum go.mod build.sh .hz conf output script .gitignore readme.md 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # *** 小众点评 2 | 3 | 本项目使用go语言重构黑马点评项目,方便使用go语言的同学学习黑马程序员的redis课程,欢迎一起交流学习。 4 | [说明文档](./doc/xzdp.md) 5 | 6 | ## 介绍 7 | 8 | 因为[黑马程序员redis教程实战篇](https://www.bilibili.com/video/BV1cr4y1671t?p=24 )使用的语言是`java`,所以想用golang重构一下。项目使用字节的[Hertz](https://www.cloudwego.io/zh/docs/hertz/)框架。 9 | 10 | 当前已经初步完成项目的基本功能,大家可以查看[issues](https://github.com/lhpqaq/xzdp-go/issues)中的需求或自行创建需求为项目提交代码,包括但不限于优化代码,添加文档,添加单元测试等。 11 | 12 | ### Start 13 | #### 前端 14 | 前端代码在`resources/nginx-1.18.0.zip`中,Windows系统可以双击`nginx.exe`运行,Mac 或 Linux 安装 nginx 后参考以下命令执行: 15 | ```shell 16 | nginx -c ~/nginx-1.18.0/conf/nginx.conf -p ~/nginx-1.18.0 17 | ``` 18 | 19 | 浏览器打开`http://127.0.0.1:8080` 20 | * 使用该前端,发布博客要使用上传图片,需要配置`nginx.conf`增加以下配置: 21 | ```shell 22 | location /imgs { 23 | proxy_pass http://127.0.0.1:8081/imgs; 24 | proxy_set_header Host $host; 25 | proxy_set_header X-Real-IP $remote_addr; 26 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 27 | proxy_set_header X-Forwarded-Proto $scheme; 28 | proxy_redirect off; 29 | } 30 | ``` 31 | #### 后端 32 | - 在mysql新建数据库表`xzdp` 33 | - 将`resources/xzdp.sql`导入到表`xzdp` 34 | - 启动`redis-server` 35 | - 复制`conf/test/conf.example.yaml`为`conf/test/conf.yaml`并修改其中的配置 36 | - `go run xzdp` 37 | 38 | ### 如何添加服务 39 | 40 | (To 不熟悉Hertz的同学) 41 | 42 | 1. 在idl目录下修改或添加[thrift](https://www.cloudwego.io/zh/docs/hertz/tutorials/toolkit/)文件 43 | 44 | 2. 修改makefile, 在update_api下添加 45 | 46 | `hz update --mod=xzdp --idl=idl/你的thrift文件 --customize_package=template/package.yaml` 47 | 48 | 3. `make update_api` 49 | 50 | 不要修改`model/SERVICE_NAME/SERVICE_NAME.go`的内容,因为会被覆盖。 51 | 52 | ### TODO 53 | 54 | - [ ] 优化代码 55 | 56 | - [ ] Anything 57 | 58 | 59 | 60 | 61 | ### 如何合作 62 | 63 | 欢迎以任何格式提交 Issue 和 PR !或者➕我v:`lhpqaq1`. 有疑问也请联系我。 64 | 65 | 点个🌟吧 😘 66 | 67 | 贡献指南:https://juejin.cn/post/7196940857308069945 68 | 69 | 70 | 71 | ## introduce 72 | 73 | - Use the [Hertz](https://github.com/cloudwego/hertz/) framework 74 | - Integration of pprof, cors, recovery, access_log, gzip and other extensions of Hertz. 75 | - Generating the base code for unit tests. 76 | - Provides basic profile functions. 77 | - Provides the most basic MVC code hierarchy. 78 | 79 | ## Directory structure 80 | 81 | | catalog | introduce | 82 | | ---- | ---- | 83 | | conf | Configuration files | 84 | | main.go | Startup file | 85 | | hertz_gen | Hertz generated model | 86 | | biz/handler | Used for request processing, validation and return of response. | 87 | | biz/service | The actual business logic. | 88 | | biz/dal | Logic for operating the storage layer | 89 | | biz/route | Routing and middleware registration | 90 | | biz/utils | Wrapped some common methods | 91 | 92 | ## How to run 93 | 94 | ```shell 95 | sh build.sh 96 | sh output/bootstrap.sh 97 | ``` 98 | -------------------------------------------------------------------------------- /resources/nginx-1.18.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lhpqaq/xzdp-go/85bd471540317d0720dc2c9c9322a2891b860737/resources/nginx-1.18.0.zip -------------------------------------------------------------------------------- /router.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | handler "xzdp/biz/handler" 9 | ) 10 | 11 | // customizeRegister registers customize routers. 12 | func customizedRegister(r *server.Hertz) { 13 | r.GET("/ping", handler.Ping) 14 | r.StaticFS("/imgs", &app.FS{Root: "upload/", GenerateIndexPages: false}) 15 | } 16 | -------------------------------------------------------------------------------- /router_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | router "xzdp/biz/router" 8 | ) 9 | 10 | // register registers all routers. 11 | func register(r *server.Hertz) { 12 | 13 | router.GeneratedRegister(r) 14 | 15 | customizedRegister(r) 16 | } 17 | -------------------------------------------------------------------------------- /script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | BinaryName=hertz_service 4 | echo "$CURDIR/bin/${BinaryName}" 5 | exec $CURDIR/bin/${BinaryName} -------------------------------------------------------------------------------- /script/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ ! -f conf/test/conf.yaml ]; then 3 | cp conf/test/conf.example.yaml conf/test/conf.yaml 4 | echo "conf/test/conf.yaml has been created from conf/test/conf.example.yaml" 5 | fi 6 | 7 | go build -v xzdp -------------------------------------------------------------------------------- /script/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 运行构建脚本 4 | sh ./script/build.sh 5 | 6 | # 测试 ./xzdp 是否能正常运行,运行2秒后退出 7 | timeout 2s ./xzdp & 8 | sleep 2 9 | kill $! 10 | 11 | # 单元测试未编写,暂时只测试能否正确运行 12 | # go test -v -cover ./... -------------------------------------------------------------------------------- /template/package.yaml: -------------------------------------------------------------------------------- 1 | layouts: 2 | - path: handler.go 3 | body: |- 4 | {{$OutDirs := GetUniqueHandlerOutDir .Methods}} 5 | package {{.PackageName}} 6 | import ( 7 | "context" 8 | 9 | "github.com/cloudwego/hertz/pkg/app" 10 | "github.com/cloudwego/hertz/pkg/protocol/consts" 11 | {{- range $k, $v := .Imports}} 12 | {{$k}} "{{$v.Package}}" 13 | {{- end}} 14 | {{- range $_, $OutDir := $OutDirs}} 15 | {{if eq $OutDir "" -}} 16 | service "{{$.ProjPackage}}/biz/service/{{$.PackageName}}" 17 | {{- else -}} 18 | service "{{$.ProjPackage}}/biz/service/{{$OutDir}}" 19 | {{- end -}} 20 | {{- end}} 21 | "{{$.ProjPackage}}/biz/utils" 22 | ) 23 | {{range $_, $MethodInfo := .Methods}} 24 | {{$MethodInfo.Comment}} 25 | func {{$MethodInfo.Name}}(ctx context.Context, c *app.RequestContext) { 26 | var err error 27 | {{if ne $MethodInfo.RequestTypeName "" -}} 28 | var req {{$MethodInfo.RequestTypeName}} 29 | err = c.BindAndValidate(&req) 30 | if err != nil { 31 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 32 | return 33 | } 34 | {{end}} 35 | {{if eq $MethodInfo.OutputDir "" -}} 36 | resp,err := service.New{{$MethodInfo.Name}}Service(ctx, c).Run(&req) 37 | if err != nil { 38 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 39 | return 40 | } 41 | {{else}} 42 | resp,err := {{$MethodInfo.OutputDir}}.New{{$MethodInfo.Name}}Service(ctx, c).Run(&req) 43 | if err != nil { 44 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 45 | return 46 | } 47 | {{end}} 48 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 49 | } 50 | {{end}} 51 | update_behavior: 52 | import_tpl: 53 | - |- 54 | {{$OutDirs := GetUniqueHandlerOutDir .Methods}} 55 | {{- range $_, $OutDir := $OutDirs}} 56 | {{if eq $OutDir "" -}} 57 | "{{$.ProjPackage}}/biz/service" 58 | {{- else -}} 59 | "{{$.ProjPackage}}/biz/service/{{$OutDir}}" 60 | {{end}} 61 | {{- end}} 62 | - path: handler_single.go 63 | body: |+ 64 | {{.Comment}} 65 | func {{.Name}}(ctx context.Context, c *app.RequestContext) { 66 | var err error 67 | {{if ne .RequestTypeName "" -}} 68 | var req {{.RequestTypeName}} 69 | err = c.BindAndValidate(&req) 70 | if err != nil { 71 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 72 | return 73 | } 74 | {{end}} 75 | {{if eq .OutputDir "" -}} 76 | resp,err := service.New{{.Name}}Service(ctx, c).Run(&req) 77 | {{else}} 78 | resp,err := {{.OutputDir}}.New{{.Name}}Service(ctx, c).Run(&req) 79 | {{end}} 80 | if err != nil { 81 | utils.SendErrResponse(ctx, c, consts.StatusOK, err) 82 | return 83 | } 84 | utils.SendSuccessResponse(ctx, c, consts.StatusOK, resp) 85 | } 86 | - path: "biz/service/{{.GenPackage}}/{{ToSnakeCase .MethodName}}.go" 87 | loop_method: true 88 | update_behavior: 89 | type: "skip" 90 | body: |- 91 | package {{.FilePackage}} 92 | import ( 93 | "context" 94 | 95 | "github.com/cloudwego/hertz/pkg/app" 96 | {{- range $k, $v := .Models}} 97 | {{$k}} "{{$v.Package}}" 98 | {{- end}} 99 | ) 100 | type {{.Name}}Service struct { 101 | RequestContext *app.RequestContext 102 | Context context.Context 103 | } 104 | 105 | func New{{.Name}}Service(Context context.Context, RequestContext *app.RequestContext) *{{.Name}}Service { 106 | return &{{.Name}}Service{RequestContext: RequestContext, Context: Context} 107 | } 108 | 109 | func (h *{{.Name}}Service) Run(req *{{.RequestTypeName}}) ( resp *{{.ReturnTypeName}}, err error) { 110 | //defer func() { 111 | // hlog.CtxInfof(h.Context, "req = %+v", req) 112 | // hlog.CtxInfof(h.Context, "resp = %+v", resp) 113 | //}() 114 | // todo edit your code 115 | return 116 | } 117 | - path: "biz/service/{{.GenPackage}}/{{ToSnakeCase .MethodName}}_test.go" 118 | loop_method: true 119 | update_behavior: 120 | type: "skip" 121 | body: |- 122 | package {{.FilePackage}} 123 | import ( 124 | "context" 125 | "testing" 126 | 127 | "github.com/cloudwego/hertz/pkg/app" 128 | "github.com/cloudwego/hertz/pkg/common/test/assert" 129 | {{- range $k, $v := .Models}} 130 | {{$k}} "{{$v.Package}}" 131 | {{- end}} 132 | ) 133 | func Test{{.Name}}Service_Run(t *testing.T) { 134 | ctx := context.Background() 135 | c := app.NewContext(1) 136 | s := New{{.Name}}Service(ctx, c) 137 | // init req and assert value 138 | req := &{{.RequestTypeName}}{} 139 | resp, err := s.Run(req) 140 | assert.DeepEqual(t, nil, resp) 141 | assert.DeepEqual(t, nil, err) 142 | // todo edit your unit test. 143 | } 144 | - path: "{{.HandlerDir}}/{{.GenPackage}}/{{ToSnakeCase .ServiceName}}_test.go" 145 | loop_service: true 146 | update_behavior: 147 | type: "append" 148 | append_key: "method" 149 | insert_key: "Test{{$.Name}}" 150 | append_tpl: |- 151 | func Test{{.Name}}(t *testing.T) { 152 | h := server.Default() 153 | h.GET("{{.Path}}", {{.Name}}) 154 | w := ut.PerformRequest(h.Engine, "{{.HTTPMethod}}", "{{.Path}}", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 155 | ut.Header{}) 156 | resp := w.Result() 157 | assert.DeepEqual(t, 201, resp.StatusCode()) 158 | assert.DeepEqual(t, "", string(resp.Body())) 159 | // todo edit your unit test. 160 | } 161 | body: |- 162 | package {{.FilePackage}} 163 | import ( 164 | "bytes" 165 | "testing" 166 | 167 | "github.com/cloudwego/hertz/pkg/app/server" 168 | "github.com/cloudwego/hertz/pkg/common/test/assert" 169 | "github.com/cloudwego/hertz/pkg/common/ut" 170 | ) 171 | {{range $_, $MethodInfo := $.Methods}} 172 | func Test{{$MethodInfo.Name}}(t *testing.T) { 173 | h := server.Default() 174 | h.GET("{{$MethodInfo.Path}}", {{$MethodInfo.Name}}) 175 | w := ut.PerformRequest(h.Engine, "{{$MethodInfo.HTTPMethod}}", "{{$MethodInfo.Path}}", &ut.Body{Body: bytes.NewBufferString(""), Len: 1}, 176 | ut.Header{}) 177 | resp := w.Result() 178 | assert.DeepEqual(t, 201, resp.StatusCode()) 179 | assert.DeepEqual(t, "", string(resp.Body())) 180 | // todo edit your unit test. 181 | } 182 | {{end}} --------------------------------------------------------------------------------