Repository: danmuking/DiTing-Go Branch: main Commit: 2187d3b9da23 Files: 147 Total size: 412.3 KB Directory structure: gitextract_mkmm_a5b/ ├── .github/ │ └── workflows/ │ └── reademe-contributors.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd/ │ └── gen/ │ └── generate.go ├── conf/ │ └── config_example.yml ├── controller/ │ ├── captcha_controller.go │ ├── chat_controller.go │ ├── contact_controller.go │ ├── friend_controller.go │ └── user_controller.go ├── dal/ │ ├── db.go │ ├── model/ │ │ ├── contact.gen.go │ │ ├── group_member.gen.go │ │ ├── message.gen.go │ │ ├── room.gen.go │ │ ├── room_friend.gen.go │ │ ├── room_group.gen.go │ │ ├── user.gen.go │ │ ├── user_apply.gen.go │ │ └── user_friend.gen.go │ └── query/ │ ├── contact.gen.go │ ├── gen.go │ ├── group_member.gen.go │ ├── message.gen.go │ ├── room.gen.go │ ├── room_friend.gen.go │ ├── room_group.gen.go │ ├── user.gen.go │ ├── user_apply.gen.go │ └── user_friend.gen.go ├── docs/ │ ├── docs.go │ └── swagger.json ├── domain/ │ ├── dto/ │ │ ├── contact_dto.go │ │ ├── delete_friend_dto.go │ │ ├── group_dto.go │ │ └── msg_dto.go │ ├── enum/ │ │ ├── event_bus.go │ │ ├── login.go │ │ ├── message.go │ │ ├── redis.go │ │ ├── redsync.go │ │ ├── rocketmq.go │ │ ├── room.go │ │ └── user.go │ ├── model/ │ │ └── message.go │ └── vo/ │ ├── req/ │ │ ├── agree_friend_req.go │ │ ├── captcha_req.go │ │ ├── create_group_req.go │ │ ├── delete_friend_req.go │ │ ├── delete_group_req.go │ │ ├── get_group_member_list_req.go │ │ ├── get_message_list_req.go │ │ ├── get_new_content_list_req.go │ │ ├── get_new_msg_list_req.go │ │ ├── get_user_info_batch_req.go │ │ ├── get_user_info_by_name_req.go │ │ ├── grant_administrator_req.go │ │ ├── is_friend_req.go │ │ ├── join_group_req.go │ │ ├── message_req.go │ │ ├── quit_group_req.go │ │ ├── remove_administrator_req.go │ │ ├── uid_req.go │ │ ├── user_apply_req.go │ │ ├── user_cancle_req.go │ │ ├── user_login_req.go │ │ └── user_register_req.go │ └── resp/ │ ├── get_user_info_batch_resp.go │ ├── get_user_info_by_name_resp.go │ ├── message_resp.go │ ├── page_list_resp.go │ ├── pre_signed_resp.go │ ├── user_apply_resp.go │ ├── user_contact_resp.go │ └── user_login_resp.go ├── event/ │ └── listener/ │ ├── friend_apply_event.go │ ├── friend_delete_event.go │ ├── friend_new_event.go │ ├── new_msg_event.go │ └── user_login_event.go ├── global/ │ ├── init_db.go │ ├── init_distribute_lock.go │ ├── init_evenbus.go │ ├── init_log.go │ ├── init_minio.go │ ├── init_redis.go │ ├── init_rocketmq.go │ └── init_time.go ├── go.mod ├── go.sum ├── logic/ │ ├── captcha.go │ ├── login.go │ ├── register.go │ ├── user.go │ └── user_cancle.go ├── main.go ├── pkg/ │ ├── domain/ │ │ ├── enum/ │ │ │ ├── code.go │ │ │ ├── common_enum.go │ │ │ └── msg.go │ │ └── vo/ │ │ ├── req/ │ │ │ └── page_req.go │ │ └── resp/ │ │ ├── page_resp.go │ │ └── response.go │ └── utils/ │ ├── cursor_utils.go │ ├── redis.go │ ├── redis_lock.go │ └── sort.go ├── routes/ │ └── init_router.go ├── service/ │ ├── adapter/ │ │ ├── build_contact_dao_list.go │ │ ├── build_message_resp.go │ │ └── build_user_info_by_name_resp.go │ ├── captcha_service.go │ ├── contact_service.go │ ├── friend_service.go │ ├── group_service.go │ ├── message_service.go │ ├── upload_service.go │ ├── user_service.go │ ├── user_service_integration_test.go │ └── user_service_test.go ├── sql/ │ └── sql.sql ├── tests/ │ ├── README.md │ ├── e2e/ │ │ └── user_workflow_e2e_test.go │ ├── init_test.go │ ├── integration/ │ │ └── user_integration_test.go │ ├── performance/ │ │ └── user_performance_test.go │ ├── scripts/ │ │ ├── run_e2e_test.sh │ │ ├── run_tests.bat │ │ ├── run_tests.sh │ │ └── setup_test_env.sh │ └── unit/ │ └── user_service_test.go ├── utils/ │ ├── jsonUtils/ │ │ └── json.go │ ├── jwt/ │ │ ├── jwt.go │ │ └── jwt_test.go │ ├── middleware/ │ │ ├── cors.go │ │ ├── jwt.go │ │ └── log.go │ ├── mysqlUtils.go │ ├── passwordUtils.go │ ├── redisCache/ │ │ └── remove_cache.go │ ├── redisUtils.go │ ├── setting/ │ │ └── setting.go │ └── time_utils.go └── websocket/ ├── domain/ │ ├── enum/ │ │ └── ws_type.go │ └── vo/ │ └── resp/ │ └── new_message_resp.go ├── global/ │ └── global.go └── service/ └── websocket_service.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/reademe-contributors.yml ================================================ on: push: branches: - main name: Generate a list of contributors jobs: contrib-readme-en-job: runs-on: ubuntu-latest name: A job to automate contrib in readme steps: - name: Contribute List uses: akhilmhdh/contributors-readme-action@v2.3.4 env: GITHUB_TOKEN: ${{ secrets.CONTRIBUTORS_TOKEN }} ================================================ FILE: .gitignore ================================================ # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore # # Binaries for programs and plugins /.idea/ *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ # Go workspace file go.work *.yml *.yaml *.log /conf/config.yml !/conf/config_example.yml !/.github/workflows/reademe-contributors.yml ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================
|
LinYi |
Soce1lo |
WangQuanYaa |
### 捐赠
如果你觉得这个项目对你有帮助,你可以请作者喝一杯咖啡。
### 版权说明
该项目签署了MIT 授权许可,详情请参阅 [LICENSE.txt](./LICENSE)
### 鸣谢
[//]: # (- [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet))
[//]: # (- [Img Shields](https://shields.io))
[//]: # (- [Choose an Open Source License](https://choosealicense.com))
[//]: # (- [GitHub Pages](https://pages.github.com))
[//]: # (- [Animate.css](https://daneden.github.io/animate.css))
[//]: # (- [xxxxxxxxxxxxxx](https://connoratherton.com/loaders))
================================================
FILE: cmd/gen/generate.go
================================================
package main
// gorm gen configure
import (
_ "DiTing-Go/pkg/setting"
"fmt"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gen"
"gorm.io/gorm"
)
var MySQLDSN = fmt.Sprintf("%s:%s@tcp(%s:%s)/DiTing?charset=utf8mb4&parseTime=True", viper.GetString("mysql.username"), viper.GetString("mysql.password"), viper.GetString("mysql.host"), viper.GetString("mysql.port"))
func connectDB(dsn string) *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
panic(fmt.Errorf("connect db fail: %w", err))
}
return db
}
func main() {
println(MySQLDSN)
// 指定生成代码的具体相对目录(相对当前文件),默认为:./query
// 默认生成需要使用WithContext之后才可以查询的代码,但可以通过设置gen.WithoutContext禁用该模式
g := gen.NewGenerator(gen.Config{
// 默认会在 OutPath 目录生成CRUD代码,并且同目录下生成 model 包
// 所以OutPath最终package不能设置为model,在有数据库表同步的情况下会产生冲突
// 若一定要使用可以通过ModelPkgPath单独指定model package的名称
OutPath: "./dal/query",
/* ModelPkgPath: "dal/model"*/
// gen.WithoutContext:禁用WithContext模式
// gen.WithDefaultQuery:生成一个全局Query对象Q
// gen.WithQueryInterface:生成Query接口
Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
})
// 通常复用项目中已有的SQL连接配置db(*gorm.DB)
// 非必需,但如果需要复用连接时的gorm.Config或需要连接数据库同步表信息则必须设置
g.UseDB(connectDB(MySQLDSN))
// 从连接的数据库为所有表生成Model结构体和CRUD代码
// 也可以手动指定需要生成代码的数据表
g.ApplyBasic(g.GenerateAllTable()...)
// 执行并生成代码
g.Execute()
}
================================================
FILE: conf/config_example.yml
================================================
mysql:
host: xxx.xxx.xxx.xx
port: 3306
username: diting
password: diting
jwt:
secret: diting
log:
log_file_path: ./logs
log_file_name: diting.log
minio:
accessKey: xxx
accessSecret: xxx
endPoint: xxx.xxx.xxx.xxx:9000
useSSL: false
redis:
host: xxxx
password: xxxxx
rocketmq:
host: xxx.xxx.xxx.xxx:9000
group: diting
================================================
FILE: controller/captcha_controller.go
================================================
package controller
import (
"DiTing-Go/domain/vo/req"
"DiTing-Go/pkg/domain/vo/resp"
"DiTing-Go/service"
"github.com/gin-gonic/gin"
)
func CaptchaController(c *gin.Context) {
captchaReq := req.CaptchaReq{}
if err := c.ShouldBind(&captchaReq); err != nil {
resp.ErrorResponse(c, "参数错误")
c.Abort()
return
}
response, err := service.CaptchaService(captchaReq)
if err != nil {
c.Abort()
resp.ReturnErrorResponse(c, response)
return
}
resp.ReturnSuccessResponse(c, response)
}
================================================
FILE: controller/chat_controller.go
================================================
package controller
//
//import (
// "DiTing-Go/domain/vo/req"
// "DiTing-Go/pkg/domain/vo/resp"
// "DiTing-Go/service"
// "github.com/gin-gonic/gin"
//)
//
//func SendMessageController(c *gin.Context) {
// uid := c.GetInt64("uid")
// messageReq := req.MessageReq{}
// if err := c.ShouldBind(&messageReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.SendTextMsgService(uid, messageReq)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
================================================
FILE: controller/contact_controller.go
================================================
package controller
//
//import (
// "DiTing-Go/domain/vo/req"
// "DiTing-Go/global"
// pkgReq "DiTing-Go/pkg/domain/vo/req"
// "DiTing-Go/pkg/domain/vo/resp"
// "DiTing-Go/service"
// "github.com/gin-gonic/gin"
//)
//
//func GetUserInfoBatchController(c *gin.Context) {
// getUserInfoBatchReq := req.GetUserInfoBatchReq{}
// if err := c.ShouldBind(&getUserInfoBatchReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.GetUserInfoBatchService(getUserInfoBatchReq)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
// return
//}
//
//func GetContactListController(c *gin.Context) {
// uid := c.GetInt64("uid")
// // 游标翻页
// // 默认值
// var cursor *string = nil
// var pageSize int = 20
// pageRequest := pkgReq.PageReq{
// Cursor: cursor,
// PageSize: pageSize,
// }
// if err := c.ShouldBindQuery(&pageRequest); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.GetContactListService(uid, pageRequest)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//func GetNewContactListController(c *gin.Context) {
// uid := c.GetInt64("uid")
//
// getNewContentListReq := req.GetNewContentListReq{}
// if err := c.ShouldBindQuery(&getNewContentListReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.GetNewContactListService(uid, getNewContentListReq.Timestamp)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//func GetNewMsgListController(c *gin.Context) {
//
// getNewMsgListReq := req.GetNewMsgListReq{}
// if err := c.ShouldBindQuery(&getNewMsgListReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.GetNewMsgService(getNewMsgListReq.MsgId, getNewMsgListReq.RoomId)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//func CreateGroupController(c *gin.Context) {
// uid := c.GetInt64("uid")
// creatGroupReq := req.CreateGroupReq{}
// if err := c.ShouldBind(&creatGroupReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
// response, err := service.CreateGroupService(uid, creatGroupReq.UidList)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
================================================
FILE: controller/friend_controller.go
================================================
package controller
//
//import (
// "DiTing-Go/domain/vo/req"
// pkgReq "DiTing-Go/pkg/domain/vo/req"
// "DiTing-Go/pkg/domain/vo/resp"
// "DiTing-Go/service"
// "github.com/gin-gonic/gin"
//)
//
//// ApplyFriendController 添加好友
////
//// @Summary 添加好友
//// @Produce json
//// @Param uid body int true "好友uid"
//// @Param msg body string true "验证消息"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/user/add [post]
//func ApplyFriendController(c *gin.Context) {
// uid := c.GetInt64("uid")
// applyReq := req.UserApplyReq{}
// if err := c.ShouldBind(&applyReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.ApplyFriendService(uid, applyReq)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
// return
//}
//
//// DeleteFriendController 删除好友
////
//// @Summary 删除好友
//// @Produce json
//// @Param uid body int true "好友uid"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/user/delete/:uid [delete]
//func DeleteFriendController(c *gin.Context) {
// uid := c.GetInt64("uid")
// deleteFriendReq := req.DeleteFriendReq{}
// if err := c.ShouldBind(&deleteFriendReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// return
// }
// response, err := service.DeleteFriendService(uid, deleteFriendReq)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//// AgreeFriendController 同意好友申请
////
//// @Summary 同意好友申请
//// @Produce json
//// @Param uid body int true "好友uid"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/user/agree [put]
//func AgreeFriendController(c *gin.Context) {
// uid := c.GetInt64("uid")
// agreeFriendReq := req.AgreeFriendReq{}
// if err := c.ShouldBind(&agreeFriendReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// response, err := service.AgreeFriendService(uid, agreeFriendReq.Uid)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//// GetUserApplyController 同意好友申请
////
//// @Summary 同意好友申请
//// @Produce json
//// @Param uid body int true "好友uid"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/user/getApplyList [get]
//func GetUserApplyController(c *gin.Context) {
// uid := c.GetInt64("uid")
// pageRequest := pkgReq.PageReq{}
// if err := c.ShouldBindQuery(&pageRequest); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// return
// }
// response, err := service.GetUserApplyService(uid, pageRequest)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
//
// resp.ReturnSuccessResponse(c, response)
//}
//
//// IsFriendController 是否为好友关系
////
//// @Summary 是否为好友关系
//// @Produce json
//// @Param uid body int true "好友uid"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/user/isFriend/:friendUid [get]
//func IsFriendController(c *gin.Context) {
// uid := c.GetInt64("uid")
// isFriendReq := req.IsFriendReq{}
// if err := c.ShouldBindUri(&isFriendReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// return
// }
// response, err := service.IsFriendService(uid, isFriendReq.FriendUid)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//// UnreadApplyNumController 好友申请未读数量
////
//// @Summary 好友申请未读数量
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/user/unreadApplyNum [get]
//func UnreadApplyNumController(c *gin.Context) {
// uid := c.GetInt64("uid")
//
// response, err := service.UnreadApplyNumService(uid)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//func GetFriendListController(c *gin.Context) {
// uid := c.GetInt64("uid")
// pageRequest := pkgReq.PageReq{}
// if err := c.ShouldBindQuery(&pageRequest); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// return
// }
// response, err := service.GetFriendListService(uid, pageRequest)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
//
//// GetUserInfoByNameController 根据好友昵称搜索好友
//func GetUserInfoByNameController(c *gin.Context) {
// uid := c.GetInt64("uid")
// getUserInfoByNameReq := req.GetUserInfoByNameReq{}
// if err := c.ShouldBindQuery(&getUserInfoByNameReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// return
// }
// response, err := service.GetUserInfoByNameService(uid, getUserInfoByNameReq.Name)
// if err != nil {
// c.Abort()
// resp.ReturnErrorResponse(c, response)
// return
// }
// resp.ReturnSuccessResponse(c, response)
//}
================================================
FILE: controller/user_controller.go
================================================
package controller
import (
"DiTing-Go/domain/vo/req"
"DiTing-Go/pkg/domain/vo/resp"
"DiTing-Go/service"
"github.com/gin-gonic/gin"
)
// RegisterController 用户注册
//
// @Summary 用户注册
// @Produce json
// @Param name body string true "用户名"
// @Param password body string true "密码"
// @Param phone body string true "手机号"
// @Param captcha body string true "验证码"
// @Success 200 {object} resp.ResponseData "成功"
// @Failure 500 {object} resp.ResponseData "内部错误"
// @Router /api/public/register [post]
func RegisterController(c *gin.Context) {
userReq := req.UserRegisterReq{}
if err := c.ShouldBind(&userReq); err != nil {
resp.ErrorResponse(c, "参数错误")
c.Abort()
return
}
response, err := service.RegisterService(userReq)
if err != nil {
c.Abort()
resp.ReturnErrorResponse(c, response)
return
}
resp.ReturnSuccessResponse(c, response)
}
// LoginController 用户登录
//
// @Summary 用户登录
// @Produce json
// @Param name body string true "用户名"
// @Param password body string true "密码"
// @Success 200 {object} resp.ResponseData "成功"
// @Failure 500 {object} resp.ResponseData "内部错误"
// @Router /api/public/login [post]
func LoginController(c *gin.Context) {
userLogin := req.UserLoginReq{}
if err := c.ShouldBind(&userLogin); err != nil { //ShouldBind()会自动推导
resp.ErrorResponse(c, "参数错误")
c.Abort()
return
}
response, err := service.LoginService(userLogin)
if err != nil {
c.Abort()
resp.ReturnErrorResponse(c, response)
return
}
resp.ReturnSuccessResponse(c, response)
}
// CancelController 注销账户
//
// @Summary 注销账户
// @Produce json
// @Param phone body string true "手机号"
// @Success 200 {object} resp.ResponseData "成功"
// @Failure 500 {object} resp.ResponseData "内部错误"
// @Router /api/public/login [post]
func CancelController(ctx *gin.Context) {
userCancel := req.UserCancelReq{}
if err := ctx.ShouldBind(&userCancel); err != nil {
resp.ErrorResponse(ctx, "参数错误")
ctx.Abort()
return
}
response, err := service.CancelService(ctx, userCancel)
if err != nil {
ctx.Abort()
resp.ReturnErrorResponse(ctx, response)
return
}
resp.ReturnSuccessResponse(ctx, response)
}
================================================
FILE: dal/db.go
================================================
package dal
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
// ConnectDB 初始化数据库连接
func ConnectDB(dsn string) *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn))
sqlDB, err := db.DB()
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
if err != nil {
panic(fmt.Errorf("connect db fail: %w", err))
}
return db
}
================================================
FILE: dal/model/contact.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameContact = "contact"
// Contact 会话列表
type Contact struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
UID int64 `gorm:"column:uid;not null;comment:uid" json:"uid"` // uid
RoomID int64 `gorm:"column:room_id;not null;comment:房间id" json:"room_id"` // 房间id
ReadTime time.Time `gorm:"column:read_time;not null;default:CURRENT_TIMESTAMP(3);comment:阅读到的时间" json:"read_time"` // 阅读到的时间
ActiveTime time.Time `gorm:"column:active_time;comment:会话内消息最后更新的时间(只有普通会话需要维护,全员会话不需要维护)" json:"active_time"` // 会话内消息最后更新的时间(只有普通会话需要维护,全员会话不需要维护)
LastMsgID int64 `gorm:"column:last_msg_id;comment:会话最新消息id" json:"last_msg_id"` // 会话最新消息id
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName Contact's table name
func (*Contact) TableName() string {
return TableNameContact
}
================================================
FILE: dal/model/group_member.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameGroupMember = "group_member"
// GroupMember 群成员表
type GroupMember struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
GroupID int64 `gorm:"column:group_id;not null;comment:群主id" json:"group_id"` // 群主id
UID int64 `gorm:"column:uid;not null;comment:成员uid" json:"uid"` // 成员uid
Role int32 `gorm:"column:role;not null;comment:成员角色 1群主 2管理员 3普通成员" json:"role"` // 成员角色 1群主 2管理员 3普通成员
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName GroupMember's table name
func (*GroupMember) TableName() string {
return TableNameGroupMember
}
================================================
FILE: dal/model/message.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameMessage = "message"
// Message 消息表
type Message struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
RoomID int64 `gorm:"column:room_id;not null;comment:会话表id" json:"room_id"` // 会话表id
FromUID int64 `gorm:"column:from_uid;not null;comment:消息发送者uid" json:"from_uid"` // 消息发送者uid
Content string `gorm:"column:content;comment:消息内容" json:"content"` // 消息内容
ReplyMsgID int64 `gorm:"column:reply_msg_id;comment:回复的消息内容" json:"reply_msg_id"` // 回复的消息内容
DeleteStatus int32 `gorm:"column:delete_status;not null;comment:消息状态 0正常 1删除" json:"delete_status"` // 消息状态 0正常 1删除
GapCount int32 `gorm:"column:gap_count;comment:与回复的消息间隔多少条" json:"gap_count"` // 与回复的消息间隔多少条
Type int32 `gorm:"column:type;default:1;comment:消息类型 1正常文本 2.撤回消息" json:"type"` // 消息类型 1正常文本 2.撤回消息
Extra string `gorm:"column:extra;comment:扩展信息" json:"extra"` // 扩展信息
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName Message's table name
func (*Message) TableName() string {
return TableNameMessage
}
================================================
FILE: dal/model/room.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameRoom = "room"
// Room 房间表
type Room struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
Type int32 `gorm:"column:type;not null;comment:房间类型 1群聊 2单聊" json:"type"` // 房间类型 1群聊 2单聊
HotFlag int32 `gorm:"column:hot_flag;comment:是否全员展示 0否 1是" json:"hot_flag"` // 是否全员展示 0否 1是
ActiveTime time.Time `gorm:"column:active_time;not null;default:CURRENT_TIMESTAMP(3);comment:群最后消息的更新时间(热点群不需要写扩散,只更新这里)" json:"active_time"` // 群最后消息的更新时间(热点群不需要写扩散,只更新这里)
LastMsgID int64 `gorm:"column:last_msg_id;comment:会话中的最后一条消息id" json:"last_msg_id"` // 会话中的最后一条消息id
ExtJSON string `gorm:"column:ext_json;comment:额外信息(根据不同类型房间有不同存储的东西)" json:"ext_json"` // 额外信息(根据不同类型房间有不同存储的东西)
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
DeleteStatus int32 `gorm:"column:delete_status;not null;comment:房间状态 0正常 1禁用(删好友了禁用)" json:"delete_status"` // 房间状态 0正常 1禁用(删好友了禁用)
}
// TableName Room's table name
func (*Room) TableName() string {
return TableNameRoom
}
================================================
FILE: dal/model/room_friend.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameRoomFriend = "room_friend"
// RoomFriend 单聊房间表
type RoomFriend struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
RoomID int64 `gorm:"column:room_id;not null;comment:房间id" json:"room_id"` // 房间id
Uid1 int64 `gorm:"column:uid1;not null;comment:uid1(更小的uid)" json:"uid1"` // uid1(更小的uid)
Uid2 int64 `gorm:"column:uid2;not null;comment:uid2(更大的uid)" json:"uid2"` // uid2(更大的uid)
RoomKey string `gorm:"column:room_key;not null;comment:房间key由两个uid拼接,先做排序uid1_uid2" json:"room_key"` // 房间key由两个uid拼接,先做排序uid1_uid2
DeleteStatus int32 `gorm:"column:delete_status;not null;comment:房间状态 0正常 1禁用(删好友了禁用)" json:"delete_status"` // 房间状态 0正常 1禁用(删好友了禁用)
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName RoomFriend's table name
func (*RoomFriend) TableName() string {
return TableNameRoomFriend
}
================================================
FILE: dal/model/room_group.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameRoomGroup = "room_group"
// RoomGroup 群聊房间表
type RoomGroup struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
RoomID int64 `gorm:"column:room_id;not null;comment:房间id" json:"room_id"` // 房间id
Name string `gorm:"column:name;not null;comment:群名称" json:"name"` // 群名称
Avatar string `gorm:"column:avatar;not null;comment:群头像" json:"avatar"` // 群头像
ExtJSON string `gorm:"column:ext_json;comment:额外信息(根据不同类型房间有不同存储的东西)" json:"ext_json"` // 额外信息(根据不同类型房间有不同存储的东西)
DeleteStatus int32 `gorm:"column:delete_status;not null;comment:逻辑删除(0-正常,1-删除)" json:"delete_status"` // 逻辑删除(0-正常,1-删除)
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName RoomGroup's table name
func (*RoomGroup) TableName() string {
return TableNameRoomGroup
}
================================================
FILE: dal/model/user.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameUser = "user"
// User 用户表
type User struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:用户id" json:"id"` // 用户id
Phone string `gorm:"column:phone;not null;comment:用户手机" json:"phone"` // 用户手机
Password string `gorm:"column:password;not null;comment:用户密码" json:"password"` // 用户密码
Name string `gorm:"column:name;not null;comment:用户昵称" json:"name"` // 用户昵称
Avatar string `gorm:"column:avatar;not null;comment:用户头像" json:"avatar"` // 用户头像
Sex int32 `gorm:"column:sex;default:3;comment:性别 1为男性,2为女性,3未知" json:"sex"` // 性别 1为男性,2为女性,3未知
ActiveStatus int32 `gorm:"column:active_status;default:2;comment:在线状态 1在线 2离线" json:"active_status"` // 在线状态 1在线 2离线
LastOptTime time.Time `gorm:"column:last_opt_time;not null;default:CURRENT_TIMESTAMP(3);comment:最后上下线时间" json:"last_opt_time"` // 最后上下线时间
IPInfo string `gorm:"column:ip_info;comment:ip信息,用于显示用户地理位置" json:"ip_info"` // ip信息,用于显示用户地理位置
ItemID int64 `gorm:"column:item_id;comment:佩戴的徽章id" json:"item_id"` // 佩戴的徽章id
Status int32 `gorm:"column:status;default:1;comment:使用状态 1.正常 2禁用" json:"status"` // 使用状态 1.正常 2禁用
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName User's table name
func (*User) TableName() string {
return TableNameUser
}
================================================
FILE: dal/model/user_apply.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameUserApply = "user_apply"
// UserApply 用户申请表
type UserApply struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
UID int64 `gorm:"column:uid;not null;comment:申请人uid" json:"uid"` // 申请人uid
Type int32 `gorm:"column:type;not null;comment:申请类型 1加好友" json:"type"` // 申请类型 1加好友
TargetID int64 `gorm:"column:target_id;not null;comment:接收人uid" json:"target_id"` // 接收人uid
Msg string `gorm:"column:msg;not null;comment:申请信息" json:"msg"` // 申请信息
Status int32 `gorm:"column:status;not null;comment:申请状态 1待审批 2同意" json:"status"` // 申请状态 1待审批 2同意
ReadStatus int32 `gorm:"column:read_status;not null;comment:阅读状态 1未读 2已读" json:"read_status"` // 阅读状态 1未读 2已读
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName UserApply's table name
func (*UserApply) TableName() string {
return TableNameUserApply
}
================================================
FILE: dal/model/user_friend.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNameUserFriend = "user_friend"
// UserFriend 用户联系人表
type UserFriend struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
UID int64 `gorm:"column:uid;not null;comment:uid" json:"uid"` // uid
FriendUID int64 `gorm:"column:friend_uid;not null;comment:好友uid" json:"friend_uid"` // 好友uid
DeleteStatus int32 `gorm:"column:delete_status;not null;comment:逻辑删除(0-正常,1-删除)" json:"delete_status"` // 逻辑删除(0-正常,1-删除)
CreateTime time.Time `gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP(3);comment:创建时间" json:"create_time"` // 创建时间
UpdateTime time.Time `gorm:"column:update_time;not null;default:CURRENT_TIMESTAMP(3);comment:修改时间" json:"update_time"` // 修改时间
}
// TableName UserFriend's table name
func (*UserFriend) TableName() string {
return TableNameUserFriend
}
================================================
FILE: dal/query/contact.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newContact(db *gorm.DB, opts ...gen.DOOption) contact {
_contact := contact{}
_contact.contactDo.UseDB(db, opts...)
_contact.contactDo.UseModel(&model.Contact{})
tableName := _contact.contactDo.TableName()
_contact.ALL = field.NewAsterisk(tableName)
_contact.ID = field.NewInt64(tableName, "id")
_contact.UID = field.NewInt64(tableName, "uid")
_contact.RoomID = field.NewInt64(tableName, "room_id")
_contact.ReadTime = field.NewTime(tableName, "read_time")
_contact.ActiveTime = field.NewTime(tableName, "active_time")
_contact.LastMsgID = field.NewInt64(tableName, "last_msg_id")
_contact.CreateTime = field.NewTime(tableName, "create_time")
_contact.UpdateTime = field.NewTime(tableName, "update_time")
_contact.fillFieldMap()
return _contact
}
// contact 会话列表
type contact struct {
contactDo contactDo
ALL field.Asterisk
ID field.Int64 // id
UID field.Int64 // uid
RoomID field.Int64 // 房间id
ReadTime field.Time // 阅读到的时间
ActiveTime field.Time // 会话内消息最后更新的时间(只有普通会话需要维护,全员会话不需要维护)
LastMsgID field.Int64 // 会话最新消息id
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (c contact) Table(newTableName string) *contact {
c.contactDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c contact) As(alias string) *contact {
c.contactDo.DO = *(c.contactDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *contact) updateTableName(table string) *contact {
c.ALL = field.NewAsterisk(table)
c.ID = field.NewInt64(table, "id")
c.UID = field.NewInt64(table, "uid")
c.RoomID = field.NewInt64(table, "room_id")
c.ReadTime = field.NewTime(table, "read_time")
c.ActiveTime = field.NewTime(table, "active_time")
c.LastMsgID = field.NewInt64(table, "last_msg_id")
c.CreateTime = field.NewTime(table, "create_time")
c.UpdateTime = field.NewTime(table, "update_time")
c.fillFieldMap()
return c
}
func (c *contact) WithContext(ctx context.Context) IContactDo { return c.contactDo.WithContext(ctx) }
func (c contact) TableName() string { return c.contactDo.TableName() }
func (c contact) Alias() string { return c.contactDo.Alias() }
func (c contact) Columns(cols ...field.Expr) gen.Columns { return c.contactDo.Columns(cols...) }
func (c *contact) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *contact) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 8)
c.fieldMap["id"] = c.ID
c.fieldMap["uid"] = c.UID
c.fieldMap["room_id"] = c.RoomID
c.fieldMap["read_time"] = c.ReadTime
c.fieldMap["active_time"] = c.ActiveTime
c.fieldMap["last_msg_id"] = c.LastMsgID
c.fieldMap["create_time"] = c.CreateTime
c.fieldMap["update_time"] = c.UpdateTime
}
func (c contact) clone(db *gorm.DB) contact {
c.contactDo.ReplaceConnPool(db.Statement.ConnPool)
return c
}
func (c contact) replaceDB(db *gorm.DB) contact {
c.contactDo.ReplaceDB(db)
return c
}
type contactDo struct{ gen.DO }
type IContactDo interface {
gen.SubQuery
Debug() IContactDo
WithContext(ctx context.Context) IContactDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IContactDo
WriteDB() IContactDo
As(alias string) gen.Dao
Session(config *gorm.Session) IContactDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IContactDo
Not(conds ...gen.Condition) IContactDo
Or(conds ...gen.Condition) IContactDo
Select(conds ...field.Expr) IContactDo
Where(conds ...gen.Condition) IContactDo
Order(conds ...field.Expr) IContactDo
Distinct(cols ...field.Expr) IContactDo
Omit(cols ...field.Expr) IContactDo
Join(table schema.Tabler, on ...field.Expr) IContactDo
LeftJoin(table schema.Tabler, on ...field.Expr) IContactDo
RightJoin(table schema.Tabler, on ...field.Expr) IContactDo
Group(cols ...field.Expr) IContactDo
Having(conds ...gen.Condition) IContactDo
Limit(limit int) IContactDo
Offset(offset int) IContactDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IContactDo
Unscoped() IContactDo
Create(values ...*model.Contact) error
CreateInBatches(values []*model.Contact, batchSize int) error
Save(values ...*model.Contact) error
First() (*model.Contact, error)
Take() (*model.Contact, error)
Last() (*model.Contact, error)
Find() ([]*model.Contact, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Contact, err error)
FindInBatches(result *[]*model.Contact, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.Contact) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IContactDo
Assign(attrs ...field.AssignExpr) IContactDo
Joins(fields ...field.RelationField) IContactDo
Preload(fields ...field.RelationField) IContactDo
FirstOrInit() (*model.Contact, error)
FirstOrCreate() (*model.Contact, error)
FindByPage(offset int, limit int) (result []*model.Contact, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IContactDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (c contactDo) Debug() IContactDo {
return c.withDO(c.DO.Debug())
}
func (c contactDo) WithContext(ctx context.Context) IContactDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c contactDo) ReadDB() IContactDo {
return c.Clauses(dbresolver.Read)
}
func (c contactDo) WriteDB() IContactDo {
return c.Clauses(dbresolver.Write)
}
func (c contactDo) Session(config *gorm.Session) IContactDo {
return c.withDO(c.DO.Session(config))
}
func (c contactDo) Clauses(conds ...clause.Expression) IContactDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c contactDo) Returning(value interface{}, columns ...string) IContactDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c contactDo) Not(conds ...gen.Condition) IContactDo {
return c.withDO(c.DO.Not(conds...))
}
func (c contactDo) Or(conds ...gen.Condition) IContactDo {
return c.withDO(c.DO.Or(conds...))
}
func (c contactDo) Select(conds ...field.Expr) IContactDo {
return c.withDO(c.DO.Select(conds...))
}
func (c contactDo) Where(conds ...gen.Condition) IContactDo {
return c.withDO(c.DO.Where(conds...))
}
func (c contactDo) Order(conds ...field.Expr) IContactDo {
return c.withDO(c.DO.Order(conds...))
}
func (c contactDo) Distinct(cols ...field.Expr) IContactDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c contactDo) Omit(cols ...field.Expr) IContactDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c contactDo) Join(table schema.Tabler, on ...field.Expr) IContactDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c contactDo) LeftJoin(table schema.Tabler, on ...field.Expr) IContactDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c contactDo) RightJoin(table schema.Tabler, on ...field.Expr) IContactDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c contactDo) Group(cols ...field.Expr) IContactDo {
return c.withDO(c.DO.Group(cols...))
}
func (c contactDo) Having(conds ...gen.Condition) IContactDo {
return c.withDO(c.DO.Having(conds...))
}
func (c contactDo) Limit(limit int) IContactDo {
return c.withDO(c.DO.Limit(limit))
}
func (c contactDo) Offset(offset int) IContactDo {
return c.withDO(c.DO.Offset(offset))
}
func (c contactDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IContactDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c contactDo) Unscoped() IContactDo {
return c.withDO(c.DO.Unscoped())
}
func (c contactDo) Create(values ...*model.Contact) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c contactDo) CreateInBatches(values []*model.Contact, batchSize int) error {
return c.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (c contactDo) Save(values ...*model.Contact) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c contactDo) First() (*model.Contact, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Contact), nil
}
}
func (c contactDo) Take() (*model.Contact, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Contact), nil
}
}
func (c contactDo) Last() (*model.Contact, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Contact), nil
}
}
func (c contactDo) Find() ([]*model.Contact, error) {
result, err := c.DO.Find()
return result.([]*model.Contact), err
}
func (c contactDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Contact, err error) {
buf := make([]*model.Contact, 0, batchSize)
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (c contactDo) FindInBatches(result *[]*model.Contact, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c contactDo) Attrs(attrs ...field.AssignExpr) IContactDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c contactDo) Assign(attrs ...field.AssignExpr) IContactDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c contactDo) Joins(fields ...field.RelationField) IContactDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c contactDo) Preload(fields ...field.RelationField) IContactDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c contactDo) FirstOrInit() (*model.Contact, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Contact), nil
}
}
func (c contactDo) FirstOrCreate() (*model.Contact, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Contact), nil
}
}
func (c contactDo) FindByPage(offset int, limit int) (result []*model.Contact, count int64, err error) {
result, err = c.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = c.Offset(-1).Limit(-1).Count()
return
}
func (c contactDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c contactDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c contactDo) Delete(models ...*model.Contact) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *contactDo) withDO(do gen.Dao) *contactDo {
c.DO = *do.(*gen.DO)
return c
}
================================================
FILE: dal/query/gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"database/sql"
"gorm.io/gorm"
"gorm.io/gen"
"gorm.io/plugin/dbresolver"
)
var (
Q = new(Query)
User *user
)
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...)
User = &Q.User
}
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
return &Query{
db: db,
User: newUser(db, opts...),
}
}
type Query struct {
db *gorm.DB
User user
}
func (q *Query) Available() bool { return q.db != nil }
func (q *Query) clone(db *gorm.DB) *Query {
return &Query{
db: db,
User: q.User.clone(db),
}
}
func (q *Query) ReadDB() *Query {
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}
func (q *Query) WriteDB() *Query {
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
return &Query{
db: db,
User: q.User.replaceDB(db),
}
}
type queryCtx struct {
User IUserDo
}
func (q *Query) WithContext(ctx context.Context) *queryCtx {
return &queryCtx{
User: q.User.WithContext(ctx),
}
}
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
tx := q.db.Begin(opts...)
return &QueryTx{Query: q.clone(tx), Error: tx.Error}
}
type QueryTx struct {
*Query
Error error
}
func (q *QueryTx) Commit() error {
return q.db.Commit().Error
}
func (q *QueryTx) Rollback() error {
return q.db.Rollback().Error
}
func (q *QueryTx) SavePoint(name string) error {
return q.db.SavePoint(name).Error
}
func (q *QueryTx) RollbackTo(name string) error {
return q.db.RollbackTo(name).Error
}
================================================
FILE: dal/query/group_member.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newGroupMember(db *gorm.DB, opts ...gen.DOOption) groupMember {
_groupMember := groupMember{}
_groupMember.groupMemberDo.UseDB(db, opts...)
_groupMember.groupMemberDo.UseModel(&model.GroupMember{})
tableName := _groupMember.groupMemberDo.TableName()
_groupMember.ALL = field.NewAsterisk(tableName)
_groupMember.ID = field.NewInt64(tableName, "id")
_groupMember.GroupID = field.NewInt64(tableName, "group_id")
_groupMember.UID = field.NewInt64(tableName, "uid")
_groupMember.Role = field.NewInt32(tableName, "role")
_groupMember.CreateTime = field.NewTime(tableName, "create_time")
_groupMember.UpdateTime = field.NewTime(tableName, "update_time")
_groupMember.fillFieldMap()
return _groupMember
}
// groupMember 群成员表
type groupMember struct {
groupMemberDo groupMemberDo
ALL field.Asterisk
ID field.Int64 // id
GroupID field.Int64 // 群主id
UID field.Int64 // 成员uid
Role field.Int32 // 成员角色 1群主 2管理员 3普通成员
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (g groupMember) Table(newTableName string) *groupMember {
g.groupMemberDo.UseTable(newTableName)
return g.updateTableName(newTableName)
}
func (g groupMember) As(alias string) *groupMember {
g.groupMemberDo.DO = *(g.groupMemberDo.As(alias).(*gen.DO))
return g.updateTableName(alias)
}
func (g *groupMember) updateTableName(table string) *groupMember {
g.ALL = field.NewAsterisk(table)
g.ID = field.NewInt64(table, "id")
g.GroupID = field.NewInt64(table, "group_id")
g.UID = field.NewInt64(table, "uid")
g.Role = field.NewInt32(table, "role")
g.CreateTime = field.NewTime(table, "create_time")
g.UpdateTime = field.NewTime(table, "update_time")
g.fillFieldMap()
return g
}
func (g *groupMember) WithContext(ctx context.Context) IGroupMemberDo {
return g.groupMemberDo.WithContext(ctx)
}
func (g groupMember) TableName() string { return g.groupMemberDo.TableName() }
func (g groupMember) Alias() string { return g.groupMemberDo.Alias() }
func (g groupMember) Columns(cols ...field.Expr) gen.Columns { return g.groupMemberDo.Columns(cols...) }
func (g *groupMember) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := g.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (g *groupMember) fillFieldMap() {
g.fieldMap = make(map[string]field.Expr, 6)
g.fieldMap["id"] = g.ID
g.fieldMap["group_id"] = g.GroupID
g.fieldMap["uid"] = g.UID
g.fieldMap["role"] = g.Role
g.fieldMap["create_time"] = g.CreateTime
g.fieldMap["update_time"] = g.UpdateTime
}
func (g groupMember) clone(db *gorm.DB) groupMember {
g.groupMemberDo.ReplaceConnPool(db.Statement.ConnPool)
return g
}
func (g groupMember) replaceDB(db *gorm.DB) groupMember {
g.groupMemberDo.ReplaceDB(db)
return g
}
type groupMemberDo struct{ gen.DO }
type IGroupMemberDo interface {
gen.SubQuery
Debug() IGroupMemberDo
WithContext(ctx context.Context) IGroupMemberDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IGroupMemberDo
WriteDB() IGroupMemberDo
As(alias string) gen.Dao
Session(config *gorm.Session) IGroupMemberDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IGroupMemberDo
Not(conds ...gen.Condition) IGroupMemberDo
Or(conds ...gen.Condition) IGroupMemberDo
Select(conds ...field.Expr) IGroupMemberDo
Where(conds ...gen.Condition) IGroupMemberDo
Order(conds ...field.Expr) IGroupMemberDo
Distinct(cols ...field.Expr) IGroupMemberDo
Omit(cols ...field.Expr) IGroupMemberDo
Join(table schema.Tabler, on ...field.Expr) IGroupMemberDo
LeftJoin(table schema.Tabler, on ...field.Expr) IGroupMemberDo
RightJoin(table schema.Tabler, on ...field.Expr) IGroupMemberDo
Group(cols ...field.Expr) IGroupMemberDo
Having(conds ...gen.Condition) IGroupMemberDo
Limit(limit int) IGroupMemberDo
Offset(offset int) IGroupMemberDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IGroupMemberDo
Unscoped() IGroupMemberDo
Create(values ...*model.GroupMember) error
CreateInBatches(values []*model.GroupMember, batchSize int) error
Save(values ...*model.GroupMember) error
First() (*model.GroupMember, error)
Take() (*model.GroupMember, error)
Last() (*model.GroupMember, error)
Find() ([]*model.GroupMember, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GroupMember, err error)
FindInBatches(result *[]*model.GroupMember, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.GroupMember) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IGroupMemberDo
Assign(attrs ...field.AssignExpr) IGroupMemberDo
Joins(fields ...field.RelationField) IGroupMemberDo
Preload(fields ...field.RelationField) IGroupMemberDo
FirstOrInit() (*model.GroupMember, error)
FirstOrCreate() (*model.GroupMember, error)
FindByPage(offset int, limit int) (result []*model.GroupMember, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IGroupMemberDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (g groupMemberDo) Debug() IGroupMemberDo {
return g.withDO(g.DO.Debug())
}
func (g groupMemberDo) WithContext(ctx context.Context) IGroupMemberDo {
return g.withDO(g.DO.WithContext(ctx))
}
func (g groupMemberDo) ReadDB() IGroupMemberDo {
return g.Clauses(dbresolver.Read)
}
func (g groupMemberDo) WriteDB() IGroupMemberDo {
return g.Clauses(dbresolver.Write)
}
func (g groupMemberDo) Session(config *gorm.Session) IGroupMemberDo {
return g.withDO(g.DO.Session(config))
}
func (g groupMemberDo) Clauses(conds ...clause.Expression) IGroupMemberDo {
return g.withDO(g.DO.Clauses(conds...))
}
func (g groupMemberDo) Returning(value interface{}, columns ...string) IGroupMemberDo {
return g.withDO(g.DO.Returning(value, columns...))
}
func (g groupMemberDo) Not(conds ...gen.Condition) IGroupMemberDo {
return g.withDO(g.DO.Not(conds...))
}
func (g groupMemberDo) Or(conds ...gen.Condition) IGroupMemberDo {
return g.withDO(g.DO.Or(conds...))
}
func (g groupMemberDo) Select(conds ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.Select(conds...))
}
func (g groupMemberDo) Where(conds ...gen.Condition) IGroupMemberDo {
return g.withDO(g.DO.Where(conds...))
}
func (g groupMemberDo) Order(conds ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.Order(conds...))
}
func (g groupMemberDo) Distinct(cols ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.Distinct(cols...))
}
func (g groupMemberDo) Omit(cols ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.Omit(cols...))
}
func (g groupMemberDo) Join(table schema.Tabler, on ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.Join(table, on...))
}
func (g groupMemberDo) LeftJoin(table schema.Tabler, on ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.LeftJoin(table, on...))
}
func (g groupMemberDo) RightJoin(table schema.Tabler, on ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.RightJoin(table, on...))
}
func (g groupMemberDo) Group(cols ...field.Expr) IGroupMemberDo {
return g.withDO(g.DO.Group(cols...))
}
func (g groupMemberDo) Having(conds ...gen.Condition) IGroupMemberDo {
return g.withDO(g.DO.Having(conds...))
}
func (g groupMemberDo) Limit(limit int) IGroupMemberDo {
return g.withDO(g.DO.Limit(limit))
}
func (g groupMemberDo) Offset(offset int) IGroupMemberDo {
return g.withDO(g.DO.Offset(offset))
}
func (g groupMemberDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IGroupMemberDo {
return g.withDO(g.DO.Scopes(funcs...))
}
func (g groupMemberDo) Unscoped() IGroupMemberDo {
return g.withDO(g.DO.Unscoped())
}
func (g groupMemberDo) Create(values ...*model.GroupMember) error {
if len(values) == 0 {
return nil
}
return g.DO.Create(values)
}
func (g groupMemberDo) CreateInBatches(values []*model.GroupMember, batchSize int) error {
return g.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (g groupMemberDo) Save(values ...*model.GroupMember) error {
if len(values) == 0 {
return nil
}
return g.DO.Save(values)
}
func (g groupMemberDo) First() (*model.GroupMember, error) {
if result, err := g.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.GroupMember), nil
}
}
func (g groupMemberDo) Take() (*model.GroupMember, error) {
if result, err := g.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.GroupMember), nil
}
}
func (g groupMemberDo) Last() (*model.GroupMember, error) {
if result, err := g.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.GroupMember), nil
}
}
func (g groupMemberDo) Find() ([]*model.GroupMember, error) {
result, err := g.DO.Find()
return result.([]*model.GroupMember), err
}
func (g groupMemberDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GroupMember, err error) {
buf := make([]*model.GroupMember, 0, batchSize)
err = g.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (g groupMemberDo) FindInBatches(result *[]*model.GroupMember, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return g.DO.FindInBatches(result, batchSize, fc)
}
func (g groupMemberDo) Attrs(attrs ...field.AssignExpr) IGroupMemberDo {
return g.withDO(g.DO.Attrs(attrs...))
}
func (g groupMemberDo) Assign(attrs ...field.AssignExpr) IGroupMemberDo {
return g.withDO(g.DO.Assign(attrs...))
}
func (g groupMemberDo) Joins(fields ...field.RelationField) IGroupMemberDo {
for _, _f := range fields {
g = *g.withDO(g.DO.Joins(_f))
}
return &g
}
func (g groupMemberDo) Preload(fields ...field.RelationField) IGroupMemberDo {
for _, _f := range fields {
g = *g.withDO(g.DO.Preload(_f))
}
return &g
}
func (g groupMemberDo) FirstOrInit() (*model.GroupMember, error) {
if result, err := g.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.GroupMember), nil
}
}
func (g groupMemberDo) FirstOrCreate() (*model.GroupMember, error) {
if result, err := g.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.GroupMember), nil
}
}
func (g groupMemberDo) FindByPage(offset int, limit int) (result []*model.GroupMember, count int64, err error) {
result, err = g.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = g.Offset(-1).Limit(-1).Count()
return
}
func (g groupMemberDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = g.Count()
if err != nil {
return
}
err = g.Offset(offset).Limit(limit).Scan(result)
return
}
func (g groupMemberDo) Scan(result interface{}) (err error) {
return g.DO.Scan(result)
}
func (g groupMemberDo) Delete(models ...*model.GroupMember) (result gen.ResultInfo, err error) {
return g.DO.Delete(models)
}
func (g *groupMemberDo) withDO(do gen.Dao) *groupMemberDo {
g.DO = *do.(*gen.DO)
return g
}
================================================
FILE: dal/query/message.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newMessage(db *gorm.DB, opts ...gen.DOOption) message {
_message := message{}
_message.messageDo.UseDB(db, opts...)
_message.messageDo.UseModel(&model.Message{})
tableName := _message.messageDo.TableName()
_message.ALL = field.NewAsterisk(tableName)
_message.ID = field.NewInt64(tableName, "id")
_message.RoomID = field.NewInt64(tableName, "room_id")
_message.FromUID = field.NewInt64(tableName, "from_uid")
_message.Content = field.NewString(tableName, "content")
_message.ReplyMsgID = field.NewInt64(tableName, "reply_msg_id")
_message.DeleteStatus = field.NewInt32(tableName, "delete_status")
_message.GapCount = field.NewInt32(tableName, "gap_count")
_message.Type = field.NewInt32(tableName, "type")
_message.Extra = field.NewString(tableName, "extra")
_message.CreateTime = field.NewTime(tableName, "create_time")
_message.UpdateTime = field.NewTime(tableName, "update_time")
_message.fillFieldMap()
return _message
}
// message 消息表
type message struct {
messageDo messageDo
ALL field.Asterisk
ID field.Int64 // id
RoomID field.Int64 // 会话表id
FromUID field.Int64 // 消息发送者uid
Content field.String // 消息内容
ReplyMsgID field.Int64 // 回复的消息内容
DeleteStatus field.Int32 // 消息状态 0正常 1删除
GapCount field.Int32 // 与回复的消息间隔多少条
Type field.Int32 // 消息类型 1正常文本 2.撤回消息
Extra field.String // 扩展信息
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (m message) Table(newTableName string) *message {
m.messageDo.UseTable(newTableName)
return m.updateTableName(newTableName)
}
func (m message) As(alias string) *message {
m.messageDo.DO = *(m.messageDo.As(alias).(*gen.DO))
return m.updateTableName(alias)
}
func (m *message) updateTableName(table string) *message {
m.ALL = field.NewAsterisk(table)
m.ID = field.NewInt64(table, "id")
m.RoomID = field.NewInt64(table, "room_id")
m.FromUID = field.NewInt64(table, "from_uid")
m.Content = field.NewString(table, "content")
m.ReplyMsgID = field.NewInt64(table, "reply_msg_id")
m.DeleteStatus = field.NewInt32(table, "delete_status")
m.GapCount = field.NewInt32(table, "gap_count")
m.Type = field.NewInt32(table, "type")
m.Extra = field.NewString(table, "extra")
m.CreateTime = field.NewTime(table, "create_time")
m.UpdateTime = field.NewTime(table, "update_time")
m.fillFieldMap()
return m
}
func (m *message) WithContext(ctx context.Context) IMessageDo { return m.messageDo.WithContext(ctx) }
func (m message) TableName() string { return m.messageDo.TableName() }
func (m message) Alias() string { return m.messageDo.Alias() }
func (m message) Columns(cols ...field.Expr) gen.Columns { return m.messageDo.Columns(cols...) }
func (m *message) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := m.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (m *message) fillFieldMap() {
m.fieldMap = make(map[string]field.Expr, 11)
m.fieldMap["id"] = m.ID
m.fieldMap["room_id"] = m.RoomID
m.fieldMap["from_uid"] = m.FromUID
m.fieldMap["content"] = m.Content
m.fieldMap["reply_msg_id"] = m.ReplyMsgID
m.fieldMap["delete_status"] = m.DeleteStatus
m.fieldMap["gap_count"] = m.GapCount
m.fieldMap["type"] = m.Type
m.fieldMap["extra"] = m.Extra
m.fieldMap["create_time"] = m.CreateTime
m.fieldMap["update_time"] = m.UpdateTime
}
func (m message) clone(db *gorm.DB) message {
m.messageDo.ReplaceConnPool(db.Statement.ConnPool)
return m
}
func (m message) replaceDB(db *gorm.DB) message {
m.messageDo.ReplaceDB(db)
return m
}
type messageDo struct{ gen.DO }
type IMessageDo interface {
gen.SubQuery
Debug() IMessageDo
WithContext(ctx context.Context) IMessageDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IMessageDo
WriteDB() IMessageDo
As(alias string) gen.Dao
Session(config *gorm.Session) IMessageDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IMessageDo
Not(conds ...gen.Condition) IMessageDo
Or(conds ...gen.Condition) IMessageDo
Select(conds ...field.Expr) IMessageDo
Where(conds ...gen.Condition) IMessageDo
Order(conds ...field.Expr) IMessageDo
Distinct(cols ...field.Expr) IMessageDo
Omit(cols ...field.Expr) IMessageDo
Join(table schema.Tabler, on ...field.Expr) IMessageDo
LeftJoin(table schema.Tabler, on ...field.Expr) IMessageDo
RightJoin(table schema.Tabler, on ...field.Expr) IMessageDo
Group(cols ...field.Expr) IMessageDo
Having(conds ...gen.Condition) IMessageDo
Limit(limit int) IMessageDo
Offset(offset int) IMessageDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IMessageDo
Unscoped() IMessageDo
Create(values ...*model.Message) error
CreateInBatches(values []*model.Message, batchSize int) error
Save(values ...*model.Message) error
First() (*model.Message, error)
Take() (*model.Message, error)
Last() (*model.Message, error)
Find() ([]*model.Message, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Message, err error)
FindInBatches(result *[]*model.Message, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.Message) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IMessageDo
Assign(attrs ...field.AssignExpr) IMessageDo
Joins(fields ...field.RelationField) IMessageDo
Preload(fields ...field.RelationField) IMessageDo
FirstOrInit() (*model.Message, error)
FirstOrCreate() (*model.Message, error)
FindByPage(offset int, limit int) (result []*model.Message, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IMessageDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (m messageDo) Debug() IMessageDo {
return m.withDO(m.DO.Debug())
}
func (m messageDo) WithContext(ctx context.Context) IMessageDo {
return m.withDO(m.DO.WithContext(ctx))
}
func (m messageDo) ReadDB() IMessageDo {
return m.Clauses(dbresolver.Read)
}
func (m messageDo) WriteDB() IMessageDo {
return m.Clauses(dbresolver.Write)
}
func (m messageDo) Session(config *gorm.Session) IMessageDo {
return m.withDO(m.DO.Session(config))
}
func (m messageDo) Clauses(conds ...clause.Expression) IMessageDo {
return m.withDO(m.DO.Clauses(conds...))
}
func (m messageDo) Returning(value interface{}, columns ...string) IMessageDo {
return m.withDO(m.DO.Returning(value, columns...))
}
func (m messageDo) Not(conds ...gen.Condition) IMessageDo {
return m.withDO(m.DO.Not(conds...))
}
func (m messageDo) Or(conds ...gen.Condition) IMessageDo {
return m.withDO(m.DO.Or(conds...))
}
func (m messageDo) Select(conds ...field.Expr) IMessageDo {
return m.withDO(m.DO.Select(conds...))
}
func (m messageDo) Where(conds ...gen.Condition) IMessageDo {
return m.withDO(m.DO.Where(conds...))
}
func (m messageDo) Order(conds ...field.Expr) IMessageDo {
return m.withDO(m.DO.Order(conds...))
}
func (m messageDo) Distinct(cols ...field.Expr) IMessageDo {
return m.withDO(m.DO.Distinct(cols...))
}
func (m messageDo) Omit(cols ...field.Expr) IMessageDo {
return m.withDO(m.DO.Omit(cols...))
}
func (m messageDo) Join(table schema.Tabler, on ...field.Expr) IMessageDo {
return m.withDO(m.DO.Join(table, on...))
}
func (m messageDo) LeftJoin(table schema.Tabler, on ...field.Expr) IMessageDo {
return m.withDO(m.DO.LeftJoin(table, on...))
}
func (m messageDo) RightJoin(table schema.Tabler, on ...field.Expr) IMessageDo {
return m.withDO(m.DO.RightJoin(table, on...))
}
func (m messageDo) Group(cols ...field.Expr) IMessageDo {
return m.withDO(m.DO.Group(cols...))
}
func (m messageDo) Having(conds ...gen.Condition) IMessageDo {
return m.withDO(m.DO.Having(conds...))
}
func (m messageDo) Limit(limit int) IMessageDo {
return m.withDO(m.DO.Limit(limit))
}
func (m messageDo) Offset(offset int) IMessageDo {
return m.withDO(m.DO.Offset(offset))
}
func (m messageDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IMessageDo {
return m.withDO(m.DO.Scopes(funcs...))
}
func (m messageDo) Unscoped() IMessageDo {
return m.withDO(m.DO.Unscoped())
}
func (m messageDo) Create(values ...*model.Message) error {
if len(values) == 0 {
return nil
}
return m.DO.Create(values)
}
func (m messageDo) CreateInBatches(values []*model.Message, batchSize int) error {
return m.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (m messageDo) Save(values ...*model.Message) error {
if len(values) == 0 {
return nil
}
return m.DO.Save(values)
}
func (m messageDo) First() (*model.Message, error) {
if result, err := m.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Message), nil
}
}
func (m messageDo) Take() (*model.Message, error) {
if result, err := m.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Message), nil
}
}
func (m messageDo) Last() (*model.Message, error) {
if result, err := m.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Message), nil
}
}
func (m messageDo) Find() ([]*model.Message, error) {
result, err := m.DO.Find()
return result.([]*model.Message), err
}
func (m messageDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Message, err error) {
buf := make([]*model.Message, 0, batchSize)
err = m.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (m messageDo) FindInBatches(result *[]*model.Message, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return m.DO.FindInBatches(result, batchSize, fc)
}
func (m messageDo) Attrs(attrs ...field.AssignExpr) IMessageDo {
return m.withDO(m.DO.Attrs(attrs...))
}
func (m messageDo) Assign(attrs ...field.AssignExpr) IMessageDo {
return m.withDO(m.DO.Assign(attrs...))
}
func (m messageDo) Joins(fields ...field.RelationField) IMessageDo {
for _, _f := range fields {
m = *m.withDO(m.DO.Joins(_f))
}
return &m
}
func (m messageDo) Preload(fields ...field.RelationField) IMessageDo {
for _, _f := range fields {
m = *m.withDO(m.DO.Preload(_f))
}
return &m
}
func (m messageDo) FirstOrInit() (*model.Message, error) {
if result, err := m.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Message), nil
}
}
func (m messageDo) FirstOrCreate() (*model.Message, error) {
if result, err := m.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Message), nil
}
}
func (m messageDo) FindByPage(offset int, limit int) (result []*model.Message, count int64, err error) {
result, err = m.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = m.Offset(-1).Limit(-1).Count()
return
}
func (m messageDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = m.Count()
if err != nil {
return
}
err = m.Offset(offset).Limit(limit).Scan(result)
return
}
func (m messageDo) Scan(result interface{}) (err error) {
return m.DO.Scan(result)
}
func (m messageDo) Delete(models ...*model.Message) (result gen.ResultInfo, err error) {
return m.DO.Delete(models)
}
func (m *messageDo) withDO(do gen.Dao) *messageDo {
m.DO = *do.(*gen.DO)
return m
}
================================================
FILE: dal/query/room.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newRoom(db *gorm.DB, opts ...gen.DOOption) room {
_room := room{}
_room.roomDo.UseDB(db, opts...)
_room.roomDo.UseModel(&model.Room{})
tableName := _room.roomDo.TableName()
_room.ALL = field.NewAsterisk(tableName)
_room.ID = field.NewInt64(tableName, "id")
_room.Type = field.NewInt32(tableName, "type")
_room.HotFlag = field.NewInt32(tableName, "hot_flag")
_room.ActiveTime = field.NewTime(tableName, "active_time")
_room.LastMsgID = field.NewInt64(tableName, "last_msg_id")
_room.ExtJSON = field.NewString(tableName, "ext_json")
_room.CreateTime = field.NewTime(tableName, "create_time")
_room.UpdateTime = field.NewTime(tableName, "update_time")
_room.DeleteStatus = field.NewInt32(tableName, "delete_status")
_room.fillFieldMap()
return _room
}
// room 房间表
type room struct {
roomDo roomDo
ALL field.Asterisk
ID field.Int64 // id
Type field.Int32 // 房间类型 1群聊 2单聊
HotFlag field.Int32 // 是否全员展示 0否 1是
ActiveTime field.Time // 群最后消息的更新时间(热点群不需要写扩散,只更新这里)
LastMsgID field.Int64 // 会话中的最后一条消息id
ExtJSON field.String // 额外信息(根据不同类型房间有不同存储的东西)
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
DeleteStatus field.Int32 // 房间状态 0正常 1禁用(删好友了禁用)
fieldMap map[string]field.Expr
}
func (r room) Table(newTableName string) *room {
r.roomDo.UseTable(newTableName)
return r.updateTableName(newTableName)
}
func (r room) As(alias string) *room {
r.roomDo.DO = *(r.roomDo.As(alias).(*gen.DO))
return r.updateTableName(alias)
}
func (r *room) updateTableName(table string) *room {
r.ALL = field.NewAsterisk(table)
r.ID = field.NewInt64(table, "id")
r.Type = field.NewInt32(table, "type")
r.HotFlag = field.NewInt32(table, "hot_flag")
r.ActiveTime = field.NewTime(table, "active_time")
r.LastMsgID = field.NewInt64(table, "last_msg_id")
r.ExtJSON = field.NewString(table, "ext_json")
r.CreateTime = field.NewTime(table, "create_time")
r.UpdateTime = field.NewTime(table, "update_time")
r.DeleteStatus = field.NewInt32(table, "delete_status")
r.fillFieldMap()
return r
}
func (r *room) WithContext(ctx context.Context) IRoomDo { return r.roomDo.WithContext(ctx) }
func (r room) TableName() string { return r.roomDo.TableName() }
func (r room) Alias() string { return r.roomDo.Alias() }
func (r room) Columns(cols ...field.Expr) gen.Columns { return r.roomDo.Columns(cols...) }
func (r *room) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := r.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (r *room) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 9)
r.fieldMap["id"] = r.ID
r.fieldMap["type"] = r.Type
r.fieldMap["hot_flag"] = r.HotFlag
r.fieldMap["active_time"] = r.ActiveTime
r.fieldMap["last_msg_id"] = r.LastMsgID
r.fieldMap["ext_json"] = r.ExtJSON
r.fieldMap["create_time"] = r.CreateTime
r.fieldMap["update_time"] = r.UpdateTime
r.fieldMap["delete_status"] = r.DeleteStatus
}
func (r room) clone(db *gorm.DB) room {
r.roomDo.ReplaceConnPool(db.Statement.ConnPool)
return r
}
func (r room) replaceDB(db *gorm.DB) room {
r.roomDo.ReplaceDB(db)
return r
}
type roomDo struct{ gen.DO }
type IRoomDo interface {
gen.SubQuery
Debug() IRoomDo
WithContext(ctx context.Context) IRoomDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IRoomDo
WriteDB() IRoomDo
As(alias string) gen.Dao
Session(config *gorm.Session) IRoomDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IRoomDo
Not(conds ...gen.Condition) IRoomDo
Or(conds ...gen.Condition) IRoomDo
Select(conds ...field.Expr) IRoomDo
Where(conds ...gen.Condition) IRoomDo
Order(conds ...field.Expr) IRoomDo
Distinct(cols ...field.Expr) IRoomDo
Omit(cols ...field.Expr) IRoomDo
Join(table schema.Tabler, on ...field.Expr) IRoomDo
LeftJoin(table schema.Tabler, on ...field.Expr) IRoomDo
RightJoin(table schema.Tabler, on ...field.Expr) IRoomDo
Group(cols ...field.Expr) IRoomDo
Having(conds ...gen.Condition) IRoomDo
Limit(limit int) IRoomDo
Offset(offset int) IRoomDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IRoomDo
Unscoped() IRoomDo
Create(values ...*model.Room) error
CreateInBatches(values []*model.Room, batchSize int) error
Save(values ...*model.Room) error
First() (*model.Room, error)
Take() (*model.Room, error)
Last() (*model.Room, error)
Find() ([]*model.Room, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Room, err error)
FindInBatches(result *[]*model.Room, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.Room) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IRoomDo
Assign(attrs ...field.AssignExpr) IRoomDo
Joins(fields ...field.RelationField) IRoomDo
Preload(fields ...field.RelationField) IRoomDo
FirstOrInit() (*model.Room, error)
FirstOrCreate() (*model.Room, error)
FindByPage(offset int, limit int) (result []*model.Room, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IRoomDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (r roomDo) Debug() IRoomDo {
return r.withDO(r.DO.Debug())
}
func (r roomDo) WithContext(ctx context.Context) IRoomDo {
return r.withDO(r.DO.WithContext(ctx))
}
func (r roomDo) ReadDB() IRoomDo {
return r.Clauses(dbresolver.Read)
}
func (r roomDo) WriteDB() IRoomDo {
return r.Clauses(dbresolver.Write)
}
func (r roomDo) Session(config *gorm.Session) IRoomDo {
return r.withDO(r.DO.Session(config))
}
func (r roomDo) Clauses(conds ...clause.Expression) IRoomDo {
return r.withDO(r.DO.Clauses(conds...))
}
func (r roomDo) Returning(value interface{}, columns ...string) IRoomDo {
return r.withDO(r.DO.Returning(value, columns...))
}
func (r roomDo) Not(conds ...gen.Condition) IRoomDo {
return r.withDO(r.DO.Not(conds...))
}
func (r roomDo) Or(conds ...gen.Condition) IRoomDo {
return r.withDO(r.DO.Or(conds...))
}
func (r roomDo) Select(conds ...field.Expr) IRoomDo {
return r.withDO(r.DO.Select(conds...))
}
func (r roomDo) Where(conds ...gen.Condition) IRoomDo {
return r.withDO(r.DO.Where(conds...))
}
func (r roomDo) Order(conds ...field.Expr) IRoomDo {
return r.withDO(r.DO.Order(conds...))
}
func (r roomDo) Distinct(cols ...field.Expr) IRoomDo {
return r.withDO(r.DO.Distinct(cols...))
}
func (r roomDo) Omit(cols ...field.Expr) IRoomDo {
return r.withDO(r.DO.Omit(cols...))
}
func (r roomDo) Join(table schema.Tabler, on ...field.Expr) IRoomDo {
return r.withDO(r.DO.Join(table, on...))
}
func (r roomDo) LeftJoin(table schema.Tabler, on ...field.Expr) IRoomDo {
return r.withDO(r.DO.LeftJoin(table, on...))
}
func (r roomDo) RightJoin(table schema.Tabler, on ...field.Expr) IRoomDo {
return r.withDO(r.DO.RightJoin(table, on...))
}
func (r roomDo) Group(cols ...field.Expr) IRoomDo {
return r.withDO(r.DO.Group(cols...))
}
func (r roomDo) Having(conds ...gen.Condition) IRoomDo {
return r.withDO(r.DO.Having(conds...))
}
func (r roomDo) Limit(limit int) IRoomDo {
return r.withDO(r.DO.Limit(limit))
}
func (r roomDo) Offset(offset int) IRoomDo {
return r.withDO(r.DO.Offset(offset))
}
func (r roomDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IRoomDo {
return r.withDO(r.DO.Scopes(funcs...))
}
func (r roomDo) Unscoped() IRoomDo {
return r.withDO(r.DO.Unscoped())
}
func (r roomDo) Create(values ...*model.Room) error {
if len(values) == 0 {
return nil
}
return r.DO.Create(values)
}
func (r roomDo) CreateInBatches(values []*model.Room, batchSize int) error {
return r.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (r roomDo) Save(values ...*model.Room) error {
if len(values) == 0 {
return nil
}
return r.DO.Save(values)
}
func (r roomDo) First() (*model.Room, error) {
if result, err := r.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Room), nil
}
}
func (r roomDo) Take() (*model.Room, error) {
if result, err := r.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Room), nil
}
}
func (r roomDo) Last() (*model.Room, error) {
if result, err := r.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Room), nil
}
}
func (r roomDo) Find() ([]*model.Room, error) {
result, err := r.DO.Find()
return result.([]*model.Room), err
}
func (r roomDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Room, err error) {
buf := make([]*model.Room, 0, batchSize)
err = r.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (r roomDo) FindInBatches(result *[]*model.Room, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return r.DO.FindInBatches(result, batchSize, fc)
}
func (r roomDo) Attrs(attrs ...field.AssignExpr) IRoomDo {
return r.withDO(r.DO.Attrs(attrs...))
}
func (r roomDo) Assign(attrs ...field.AssignExpr) IRoomDo {
return r.withDO(r.DO.Assign(attrs...))
}
func (r roomDo) Joins(fields ...field.RelationField) IRoomDo {
for _, _f := range fields {
r = *r.withDO(r.DO.Joins(_f))
}
return &r
}
func (r roomDo) Preload(fields ...field.RelationField) IRoomDo {
for _, _f := range fields {
r = *r.withDO(r.DO.Preload(_f))
}
return &r
}
func (r roomDo) FirstOrInit() (*model.Room, error) {
if result, err := r.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Room), nil
}
}
func (r roomDo) FirstOrCreate() (*model.Room, error) {
if result, err := r.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Room), nil
}
}
func (r roomDo) FindByPage(offset int, limit int) (result []*model.Room, count int64, err error) {
result, err = r.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = r.Offset(-1).Limit(-1).Count()
return
}
func (r roomDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = r.Count()
if err != nil {
return
}
err = r.Offset(offset).Limit(limit).Scan(result)
return
}
func (r roomDo) Scan(result interface{}) (err error) {
return r.DO.Scan(result)
}
func (r roomDo) Delete(models ...*model.Room) (result gen.ResultInfo, err error) {
return r.DO.Delete(models)
}
func (r *roomDo) withDO(do gen.Dao) *roomDo {
r.DO = *do.(*gen.DO)
return r
}
================================================
FILE: dal/query/room_friend.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newRoomFriend(db *gorm.DB, opts ...gen.DOOption) roomFriend {
_roomFriend := roomFriend{}
_roomFriend.roomFriendDo.UseDB(db, opts...)
_roomFriend.roomFriendDo.UseModel(&model.RoomFriend{})
tableName := _roomFriend.roomFriendDo.TableName()
_roomFriend.ALL = field.NewAsterisk(tableName)
_roomFriend.ID = field.NewInt64(tableName, "id")
_roomFriend.RoomID = field.NewInt64(tableName, "room_id")
_roomFriend.Uid1 = field.NewInt64(tableName, "uid1")
_roomFriend.Uid2 = field.NewInt64(tableName, "uid2")
_roomFriend.RoomKey = field.NewString(tableName, "room_key")
_roomFriend.DeleteStatus = field.NewInt32(tableName, "delete_status")
_roomFriend.CreateTime = field.NewTime(tableName, "create_time")
_roomFriend.UpdateTime = field.NewTime(tableName, "update_time")
_roomFriend.fillFieldMap()
return _roomFriend
}
// roomFriend 单聊房间表
type roomFriend struct {
roomFriendDo roomFriendDo
ALL field.Asterisk
ID field.Int64 // id
RoomID field.Int64 // 房间id
Uid1 field.Int64 // uid1(更小的uid)
Uid2 field.Int64 // uid2(更大的uid)
RoomKey field.String // 房间key由两个uid拼接,先做排序uid1_uid2
DeleteStatus field.Int32 // 房间状态 0正常 1禁用(删好友了禁用)
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (r roomFriend) Table(newTableName string) *roomFriend {
r.roomFriendDo.UseTable(newTableName)
return r.updateTableName(newTableName)
}
func (r roomFriend) As(alias string) *roomFriend {
r.roomFriendDo.DO = *(r.roomFriendDo.As(alias).(*gen.DO))
return r.updateTableName(alias)
}
func (r *roomFriend) updateTableName(table string) *roomFriend {
r.ALL = field.NewAsterisk(table)
r.ID = field.NewInt64(table, "id")
r.RoomID = field.NewInt64(table, "room_id")
r.Uid1 = field.NewInt64(table, "uid1")
r.Uid2 = field.NewInt64(table, "uid2")
r.RoomKey = field.NewString(table, "room_key")
r.DeleteStatus = field.NewInt32(table, "delete_status")
r.CreateTime = field.NewTime(table, "create_time")
r.UpdateTime = field.NewTime(table, "update_time")
r.fillFieldMap()
return r
}
func (r *roomFriend) WithContext(ctx context.Context) IRoomFriendDo {
return r.roomFriendDo.WithContext(ctx)
}
func (r roomFriend) TableName() string { return r.roomFriendDo.TableName() }
func (r roomFriend) Alias() string { return r.roomFriendDo.Alias() }
func (r roomFriend) Columns(cols ...field.Expr) gen.Columns { return r.roomFriendDo.Columns(cols...) }
func (r *roomFriend) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := r.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (r *roomFriend) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 8)
r.fieldMap["id"] = r.ID
r.fieldMap["room_id"] = r.RoomID
r.fieldMap["uid1"] = r.Uid1
r.fieldMap["uid2"] = r.Uid2
r.fieldMap["room_key"] = r.RoomKey
r.fieldMap["delete_status"] = r.DeleteStatus
r.fieldMap["create_time"] = r.CreateTime
r.fieldMap["update_time"] = r.UpdateTime
}
func (r roomFriend) clone(db *gorm.DB) roomFriend {
r.roomFriendDo.ReplaceConnPool(db.Statement.ConnPool)
return r
}
func (r roomFriend) replaceDB(db *gorm.DB) roomFriend {
r.roomFriendDo.ReplaceDB(db)
return r
}
type roomFriendDo struct{ gen.DO }
type IRoomFriendDo interface {
gen.SubQuery
Debug() IRoomFriendDo
WithContext(ctx context.Context) IRoomFriendDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IRoomFriendDo
WriteDB() IRoomFriendDo
As(alias string) gen.Dao
Session(config *gorm.Session) IRoomFriendDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IRoomFriendDo
Not(conds ...gen.Condition) IRoomFriendDo
Or(conds ...gen.Condition) IRoomFriendDo
Select(conds ...field.Expr) IRoomFriendDo
Where(conds ...gen.Condition) IRoomFriendDo
Order(conds ...field.Expr) IRoomFriendDo
Distinct(cols ...field.Expr) IRoomFriendDo
Omit(cols ...field.Expr) IRoomFriendDo
Join(table schema.Tabler, on ...field.Expr) IRoomFriendDo
LeftJoin(table schema.Tabler, on ...field.Expr) IRoomFriendDo
RightJoin(table schema.Tabler, on ...field.Expr) IRoomFriendDo
Group(cols ...field.Expr) IRoomFriendDo
Having(conds ...gen.Condition) IRoomFriendDo
Limit(limit int) IRoomFriendDo
Offset(offset int) IRoomFriendDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IRoomFriendDo
Unscoped() IRoomFriendDo
Create(values ...*model.RoomFriend) error
CreateInBatches(values []*model.RoomFriend, batchSize int) error
Save(values ...*model.RoomFriend) error
First() (*model.RoomFriend, error)
Take() (*model.RoomFriend, error)
Last() (*model.RoomFriend, error)
Find() ([]*model.RoomFriend, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.RoomFriend, err error)
FindInBatches(result *[]*model.RoomFriend, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.RoomFriend) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IRoomFriendDo
Assign(attrs ...field.AssignExpr) IRoomFriendDo
Joins(fields ...field.RelationField) IRoomFriendDo
Preload(fields ...field.RelationField) IRoomFriendDo
FirstOrInit() (*model.RoomFriend, error)
FirstOrCreate() (*model.RoomFriend, error)
FindByPage(offset int, limit int) (result []*model.RoomFriend, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IRoomFriendDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (r roomFriendDo) Debug() IRoomFriendDo {
return r.withDO(r.DO.Debug())
}
func (r roomFriendDo) WithContext(ctx context.Context) IRoomFriendDo {
return r.withDO(r.DO.WithContext(ctx))
}
func (r roomFriendDo) ReadDB() IRoomFriendDo {
return r.Clauses(dbresolver.Read)
}
func (r roomFriendDo) WriteDB() IRoomFriendDo {
return r.Clauses(dbresolver.Write)
}
func (r roomFriendDo) Session(config *gorm.Session) IRoomFriendDo {
return r.withDO(r.DO.Session(config))
}
func (r roomFriendDo) Clauses(conds ...clause.Expression) IRoomFriendDo {
return r.withDO(r.DO.Clauses(conds...))
}
func (r roomFriendDo) Returning(value interface{}, columns ...string) IRoomFriendDo {
return r.withDO(r.DO.Returning(value, columns...))
}
func (r roomFriendDo) Not(conds ...gen.Condition) IRoomFriendDo {
return r.withDO(r.DO.Not(conds...))
}
func (r roomFriendDo) Or(conds ...gen.Condition) IRoomFriendDo {
return r.withDO(r.DO.Or(conds...))
}
func (r roomFriendDo) Select(conds ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.Select(conds...))
}
func (r roomFriendDo) Where(conds ...gen.Condition) IRoomFriendDo {
return r.withDO(r.DO.Where(conds...))
}
func (r roomFriendDo) Order(conds ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.Order(conds...))
}
func (r roomFriendDo) Distinct(cols ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.Distinct(cols...))
}
func (r roomFriendDo) Omit(cols ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.Omit(cols...))
}
func (r roomFriendDo) Join(table schema.Tabler, on ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.Join(table, on...))
}
func (r roomFriendDo) LeftJoin(table schema.Tabler, on ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.LeftJoin(table, on...))
}
func (r roomFriendDo) RightJoin(table schema.Tabler, on ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.RightJoin(table, on...))
}
func (r roomFriendDo) Group(cols ...field.Expr) IRoomFriendDo {
return r.withDO(r.DO.Group(cols...))
}
func (r roomFriendDo) Having(conds ...gen.Condition) IRoomFriendDo {
return r.withDO(r.DO.Having(conds...))
}
func (r roomFriendDo) Limit(limit int) IRoomFriendDo {
return r.withDO(r.DO.Limit(limit))
}
func (r roomFriendDo) Offset(offset int) IRoomFriendDo {
return r.withDO(r.DO.Offset(offset))
}
func (r roomFriendDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IRoomFriendDo {
return r.withDO(r.DO.Scopes(funcs...))
}
func (r roomFriendDo) Unscoped() IRoomFriendDo {
return r.withDO(r.DO.Unscoped())
}
func (r roomFriendDo) Create(values ...*model.RoomFriend) error {
if len(values) == 0 {
return nil
}
return r.DO.Create(values)
}
func (r roomFriendDo) CreateInBatches(values []*model.RoomFriend, batchSize int) error {
return r.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (r roomFriendDo) Save(values ...*model.RoomFriend) error {
if len(values) == 0 {
return nil
}
return r.DO.Save(values)
}
func (r roomFriendDo) First() (*model.RoomFriend, error) {
if result, err := r.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.RoomFriend), nil
}
}
func (r roomFriendDo) Take() (*model.RoomFriend, error) {
if result, err := r.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.RoomFriend), nil
}
}
func (r roomFriendDo) Last() (*model.RoomFriend, error) {
if result, err := r.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.RoomFriend), nil
}
}
func (r roomFriendDo) Find() ([]*model.RoomFriend, error) {
result, err := r.DO.Find()
return result.([]*model.RoomFriend), err
}
func (r roomFriendDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.RoomFriend, err error) {
buf := make([]*model.RoomFriend, 0, batchSize)
err = r.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (r roomFriendDo) FindInBatches(result *[]*model.RoomFriend, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return r.DO.FindInBatches(result, batchSize, fc)
}
func (r roomFriendDo) Attrs(attrs ...field.AssignExpr) IRoomFriendDo {
return r.withDO(r.DO.Attrs(attrs...))
}
func (r roomFriendDo) Assign(attrs ...field.AssignExpr) IRoomFriendDo {
return r.withDO(r.DO.Assign(attrs...))
}
func (r roomFriendDo) Joins(fields ...field.RelationField) IRoomFriendDo {
for _, _f := range fields {
r = *r.withDO(r.DO.Joins(_f))
}
return &r
}
func (r roomFriendDo) Preload(fields ...field.RelationField) IRoomFriendDo {
for _, _f := range fields {
r = *r.withDO(r.DO.Preload(_f))
}
return &r
}
func (r roomFriendDo) FirstOrInit() (*model.RoomFriend, error) {
if result, err := r.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.RoomFriend), nil
}
}
func (r roomFriendDo) FirstOrCreate() (*model.RoomFriend, error) {
if result, err := r.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.RoomFriend), nil
}
}
func (r roomFriendDo) FindByPage(offset int, limit int) (result []*model.RoomFriend, count int64, err error) {
result, err = r.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = r.Offset(-1).Limit(-1).Count()
return
}
func (r roomFriendDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = r.Count()
if err != nil {
return
}
err = r.Offset(offset).Limit(limit).Scan(result)
return
}
func (r roomFriendDo) Scan(result interface{}) (err error) {
return r.DO.Scan(result)
}
func (r roomFriendDo) Delete(models ...*model.RoomFriend) (result gen.ResultInfo, err error) {
return r.DO.Delete(models)
}
func (r *roomFriendDo) withDO(do gen.Dao) *roomFriendDo {
r.DO = *do.(*gen.DO)
return r
}
================================================
FILE: dal/query/room_group.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newRoomGroup(db *gorm.DB, opts ...gen.DOOption) roomGroup {
_roomGroup := roomGroup{}
_roomGroup.roomGroupDo.UseDB(db, opts...)
_roomGroup.roomGroupDo.UseModel(&model.RoomGroup{})
tableName := _roomGroup.roomGroupDo.TableName()
_roomGroup.ALL = field.NewAsterisk(tableName)
_roomGroup.ID = field.NewInt64(tableName, "id")
_roomGroup.RoomID = field.NewInt64(tableName, "room_id")
_roomGroup.Name = field.NewString(tableName, "name")
_roomGroup.Avatar = field.NewString(tableName, "avatar")
_roomGroup.ExtJSON = field.NewString(tableName, "ext_json")
_roomGroup.DeleteStatus = field.NewInt32(tableName, "delete_status")
_roomGroup.CreateTime = field.NewTime(tableName, "create_time")
_roomGroup.UpdateTime = field.NewTime(tableName, "update_time")
_roomGroup.fillFieldMap()
return _roomGroup
}
// roomGroup 群聊房间表
type roomGroup struct {
roomGroupDo roomGroupDo
ALL field.Asterisk
ID field.Int64 // id
RoomID field.Int64 // 房间id
Name field.String // 群名称
Avatar field.String // 群头像
ExtJSON field.String // 额外信息(根据不同类型房间有不同存储的东西)
DeleteStatus field.Int32 // 逻辑删除(0-正常,1-删除)
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (r roomGroup) Table(newTableName string) *roomGroup {
r.roomGroupDo.UseTable(newTableName)
return r.updateTableName(newTableName)
}
func (r roomGroup) As(alias string) *roomGroup {
r.roomGroupDo.DO = *(r.roomGroupDo.As(alias).(*gen.DO))
return r.updateTableName(alias)
}
func (r *roomGroup) updateTableName(table string) *roomGroup {
r.ALL = field.NewAsterisk(table)
r.ID = field.NewInt64(table, "id")
r.RoomID = field.NewInt64(table, "room_id")
r.Name = field.NewString(table, "name")
r.Avatar = field.NewString(table, "avatar")
r.ExtJSON = field.NewString(table, "ext_json")
r.DeleteStatus = field.NewInt32(table, "delete_status")
r.CreateTime = field.NewTime(table, "create_time")
r.UpdateTime = field.NewTime(table, "update_time")
r.fillFieldMap()
return r
}
func (r *roomGroup) WithContext(ctx context.Context) IRoomGroupDo {
return r.roomGroupDo.WithContext(ctx)
}
func (r roomGroup) TableName() string { return r.roomGroupDo.TableName() }
func (r roomGroup) Alias() string { return r.roomGroupDo.Alias() }
func (r roomGroup) Columns(cols ...field.Expr) gen.Columns { return r.roomGroupDo.Columns(cols...) }
func (r *roomGroup) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := r.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (r *roomGroup) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 8)
r.fieldMap["id"] = r.ID
r.fieldMap["room_id"] = r.RoomID
r.fieldMap["name"] = r.Name
r.fieldMap["avatar"] = r.Avatar
r.fieldMap["ext_json"] = r.ExtJSON
r.fieldMap["delete_status"] = r.DeleteStatus
r.fieldMap["create_time"] = r.CreateTime
r.fieldMap["update_time"] = r.UpdateTime
}
func (r roomGroup) clone(db *gorm.DB) roomGroup {
r.roomGroupDo.ReplaceConnPool(db.Statement.ConnPool)
return r
}
func (r roomGroup) replaceDB(db *gorm.DB) roomGroup {
r.roomGroupDo.ReplaceDB(db)
return r
}
type roomGroupDo struct{ gen.DO }
type IRoomGroupDo interface {
gen.SubQuery
Debug() IRoomGroupDo
WithContext(ctx context.Context) IRoomGroupDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IRoomGroupDo
WriteDB() IRoomGroupDo
As(alias string) gen.Dao
Session(config *gorm.Session) IRoomGroupDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IRoomGroupDo
Not(conds ...gen.Condition) IRoomGroupDo
Or(conds ...gen.Condition) IRoomGroupDo
Select(conds ...field.Expr) IRoomGroupDo
Where(conds ...gen.Condition) IRoomGroupDo
Order(conds ...field.Expr) IRoomGroupDo
Distinct(cols ...field.Expr) IRoomGroupDo
Omit(cols ...field.Expr) IRoomGroupDo
Join(table schema.Tabler, on ...field.Expr) IRoomGroupDo
LeftJoin(table schema.Tabler, on ...field.Expr) IRoomGroupDo
RightJoin(table schema.Tabler, on ...field.Expr) IRoomGroupDo
Group(cols ...field.Expr) IRoomGroupDo
Having(conds ...gen.Condition) IRoomGroupDo
Limit(limit int) IRoomGroupDo
Offset(offset int) IRoomGroupDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IRoomGroupDo
Unscoped() IRoomGroupDo
Create(values ...*model.RoomGroup) error
CreateInBatches(values []*model.RoomGroup, batchSize int) error
Save(values ...*model.RoomGroup) error
First() (*model.RoomGroup, error)
Take() (*model.RoomGroup, error)
Last() (*model.RoomGroup, error)
Find() ([]*model.RoomGroup, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.RoomGroup, err error)
FindInBatches(result *[]*model.RoomGroup, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.RoomGroup) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IRoomGroupDo
Assign(attrs ...field.AssignExpr) IRoomGroupDo
Joins(fields ...field.RelationField) IRoomGroupDo
Preload(fields ...field.RelationField) IRoomGroupDo
FirstOrInit() (*model.RoomGroup, error)
FirstOrCreate() (*model.RoomGroup, error)
FindByPage(offset int, limit int) (result []*model.RoomGroup, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IRoomGroupDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (r roomGroupDo) Debug() IRoomGroupDo {
return r.withDO(r.DO.Debug())
}
func (r roomGroupDo) WithContext(ctx context.Context) IRoomGroupDo {
return r.withDO(r.DO.WithContext(ctx))
}
func (r roomGroupDo) ReadDB() IRoomGroupDo {
return r.Clauses(dbresolver.Read)
}
func (r roomGroupDo) WriteDB() IRoomGroupDo {
return r.Clauses(dbresolver.Write)
}
func (r roomGroupDo) Session(config *gorm.Session) IRoomGroupDo {
return r.withDO(r.DO.Session(config))
}
func (r roomGroupDo) Clauses(conds ...clause.Expression) IRoomGroupDo {
return r.withDO(r.DO.Clauses(conds...))
}
func (r roomGroupDo) Returning(value interface{}, columns ...string) IRoomGroupDo {
return r.withDO(r.DO.Returning(value, columns...))
}
func (r roomGroupDo) Not(conds ...gen.Condition) IRoomGroupDo {
return r.withDO(r.DO.Not(conds...))
}
func (r roomGroupDo) Or(conds ...gen.Condition) IRoomGroupDo {
return r.withDO(r.DO.Or(conds...))
}
func (r roomGroupDo) Select(conds ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.Select(conds...))
}
func (r roomGroupDo) Where(conds ...gen.Condition) IRoomGroupDo {
return r.withDO(r.DO.Where(conds...))
}
func (r roomGroupDo) Order(conds ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.Order(conds...))
}
func (r roomGroupDo) Distinct(cols ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.Distinct(cols...))
}
func (r roomGroupDo) Omit(cols ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.Omit(cols...))
}
func (r roomGroupDo) Join(table schema.Tabler, on ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.Join(table, on...))
}
func (r roomGroupDo) LeftJoin(table schema.Tabler, on ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.LeftJoin(table, on...))
}
func (r roomGroupDo) RightJoin(table schema.Tabler, on ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.RightJoin(table, on...))
}
func (r roomGroupDo) Group(cols ...field.Expr) IRoomGroupDo {
return r.withDO(r.DO.Group(cols...))
}
func (r roomGroupDo) Having(conds ...gen.Condition) IRoomGroupDo {
return r.withDO(r.DO.Having(conds...))
}
func (r roomGroupDo) Limit(limit int) IRoomGroupDo {
return r.withDO(r.DO.Limit(limit))
}
func (r roomGroupDo) Offset(offset int) IRoomGroupDo {
return r.withDO(r.DO.Offset(offset))
}
func (r roomGroupDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IRoomGroupDo {
return r.withDO(r.DO.Scopes(funcs...))
}
func (r roomGroupDo) Unscoped() IRoomGroupDo {
return r.withDO(r.DO.Unscoped())
}
func (r roomGroupDo) Create(values ...*model.RoomGroup) error {
if len(values) == 0 {
return nil
}
return r.DO.Create(values)
}
func (r roomGroupDo) CreateInBatches(values []*model.RoomGroup, batchSize int) error {
return r.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (r roomGroupDo) Save(values ...*model.RoomGroup) error {
if len(values) == 0 {
return nil
}
return r.DO.Save(values)
}
func (r roomGroupDo) First() (*model.RoomGroup, error) {
if result, err := r.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.RoomGroup), nil
}
}
func (r roomGroupDo) Take() (*model.RoomGroup, error) {
if result, err := r.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.RoomGroup), nil
}
}
func (r roomGroupDo) Last() (*model.RoomGroup, error) {
if result, err := r.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.RoomGroup), nil
}
}
func (r roomGroupDo) Find() ([]*model.RoomGroup, error) {
result, err := r.DO.Find()
return result.([]*model.RoomGroup), err
}
func (r roomGroupDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.RoomGroup, err error) {
buf := make([]*model.RoomGroup, 0, batchSize)
err = r.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (r roomGroupDo) FindInBatches(result *[]*model.RoomGroup, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return r.DO.FindInBatches(result, batchSize, fc)
}
func (r roomGroupDo) Attrs(attrs ...field.AssignExpr) IRoomGroupDo {
return r.withDO(r.DO.Attrs(attrs...))
}
func (r roomGroupDo) Assign(attrs ...field.AssignExpr) IRoomGroupDo {
return r.withDO(r.DO.Assign(attrs...))
}
func (r roomGroupDo) Joins(fields ...field.RelationField) IRoomGroupDo {
for _, _f := range fields {
r = *r.withDO(r.DO.Joins(_f))
}
return &r
}
func (r roomGroupDo) Preload(fields ...field.RelationField) IRoomGroupDo {
for _, _f := range fields {
r = *r.withDO(r.DO.Preload(_f))
}
return &r
}
func (r roomGroupDo) FirstOrInit() (*model.RoomGroup, error) {
if result, err := r.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.RoomGroup), nil
}
}
func (r roomGroupDo) FirstOrCreate() (*model.RoomGroup, error) {
if result, err := r.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.RoomGroup), nil
}
}
func (r roomGroupDo) FindByPage(offset int, limit int) (result []*model.RoomGroup, count int64, err error) {
result, err = r.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = r.Offset(-1).Limit(-1).Count()
return
}
func (r roomGroupDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = r.Count()
if err != nil {
return
}
err = r.Offset(offset).Limit(limit).Scan(result)
return
}
func (r roomGroupDo) Scan(result interface{}) (err error) {
return r.DO.Scan(result)
}
func (r roomGroupDo) Delete(models ...*model.RoomGroup) (result gen.ResultInfo, err error) {
return r.DO.Delete(models)
}
func (r *roomGroupDo) withDO(do gen.Dao) *roomGroupDo {
r.DO = *do.(*gen.DO)
return r
}
================================================
FILE: dal/query/user.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newUser(db *gorm.DB, opts ...gen.DOOption) user {
_user := user{}
_user.userDo.UseDB(db, opts...)
_user.userDo.UseModel(&model.User{})
tableName := _user.userDo.TableName()
_user.ALL = field.NewAsterisk(tableName)
_user.ID = field.NewInt64(tableName, "id")
_user.Phone = field.NewString(tableName, "phone")
_user.Password = field.NewString(tableName, "password")
_user.Name = field.NewString(tableName, "name")
_user.Avatar = field.NewString(tableName, "avatar")
_user.Sex = field.NewInt32(tableName, "sex")
_user.ActiveStatus = field.NewInt32(tableName, "active_status")
_user.LastOptTime = field.NewTime(tableName, "last_opt_time")
_user.IPInfo = field.NewString(tableName, "ip_info")
_user.ItemID = field.NewInt64(tableName, "item_id")
_user.Status = field.NewInt32(tableName, "status")
_user.CreateTime = field.NewTime(tableName, "create_time")
_user.UpdateTime = field.NewTime(tableName, "update_time")
_user.fillFieldMap()
return _user
}
// user 用户表
type user struct {
userDo userDo
ALL field.Asterisk
ID field.Int64 // 用户id
Phone field.String // 用户手机
Password field.String // 用户密码
Name field.String // 用户昵称
Avatar field.String // 用户头像
Sex field.Int32 // 性别 1为男性,2为女性,3未知
ActiveStatus field.Int32 // 在线状态 1在线 2离线
LastOptTime field.Time // 最后上下线时间
IPInfo field.String // ip信息,用于显示用户地理位置
ItemID field.Int64 // 佩戴的徽章id
Status field.Int32 // 使用状态 1.正常 2禁用
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (u user) Table(newTableName string) *user {
u.userDo.UseTable(newTableName)
return u.updateTableName(newTableName)
}
func (u user) As(alias string) *user {
u.userDo.DO = *(u.userDo.As(alias).(*gen.DO))
return u.updateTableName(alias)
}
func (u *user) updateTableName(table string) *user {
u.ALL = field.NewAsterisk(table)
u.ID = field.NewInt64(table, "id")
u.Phone = field.NewString(table, "phone")
u.Password = field.NewString(table, "password")
u.Name = field.NewString(table, "name")
u.Avatar = field.NewString(table, "avatar")
u.Sex = field.NewInt32(table, "sex")
u.ActiveStatus = field.NewInt32(table, "active_status")
u.LastOptTime = field.NewTime(table, "last_opt_time")
u.IPInfo = field.NewString(table, "ip_info")
u.ItemID = field.NewInt64(table, "item_id")
u.Status = field.NewInt32(table, "status")
u.CreateTime = field.NewTime(table, "create_time")
u.UpdateTime = field.NewTime(table, "update_time")
u.fillFieldMap()
return u
}
func (u *user) WithContext(ctx context.Context) IUserDo { return u.userDo.WithContext(ctx) }
func (u user) TableName() string { return u.userDo.TableName() }
func (u user) Alias() string { return u.userDo.Alias() }
func (u user) Columns(cols ...field.Expr) gen.Columns { return u.userDo.Columns(cols...) }
func (u *user) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := u.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (u *user) fillFieldMap() {
u.fieldMap = make(map[string]field.Expr, 13)
u.fieldMap["id"] = u.ID
u.fieldMap["phone"] = u.Phone
u.fieldMap["password"] = u.Password
u.fieldMap["name"] = u.Name
u.fieldMap["avatar"] = u.Avatar
u.fieldMap["sex"] = u.Sex
u.fieldMap["active_status"] = u.ActiveStatus
u.fieldMap["last_opt_time"] = u.LastOptTime
u.fieldMap["ip_info"] = u.IPInfo
u.fieldMap["item_id"] = u.ItemID
u.fieldMap["status"] = u.Status
u.fieldMap["create_time"] = u.CreateTime
u.fieldMap["update_time"] = u.UpdateTime
}
func (u user) clone(db *gorm.DB) user {
u.userDo.ReplaceConnPool(db.Statement.ConnPool)
return u
}
func (u user) replaceDB(db *gorm.DB) user {
u.userDo.ReplaceDB(db)
return u
}
type userDo struct{ gen.DO }
type IUserDo interface {
gen.SubQuery
Debug() IUserDo
WithContext(ctx context.Context) IUserDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IUserDo
WriteDB() IUserDo
As(alias string) gen.Dao
Session(config *gorm.Session) IUserDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IUserDo
Not(conds ...gen.Condition) IUserDo
Or(conds ...gen.Condition) IUserDo
Select(conds ...field.Expr) IUserDo
Where(conds ...gen.Condition) IUserDo
Order(conds ...field.Expr) IUserDo
Distinct(cols ...field.Expr) IUserDo
Omit(cols ...field.Expr) IUserDo
Join(table schema.Tabler, on ...field.Expr) IUserDo
LeftJoin(table schema.Tabler, on ...field.Expr) IUserDo
RightJoin(table schema.Tabler, on ...field.Expr) IUserDo
Group(cols ...field.Expr) IUserDo
Having(conds ...gen.Condition) IUserDo
Limit(limit int) IUserDo
Offset(offset int) IUserDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IUserDo
Unscoped() IUserDo
Create(values ...*model.User) error
CreateInBatches(values []*model.User, batchSize int) error
Save(values ...*model.User) error
First() (*model.User, error)
Take() (*model.User, error)
Last() (*model.User, error)
Find() ([]*model.User, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.User, err error)
FindInBatches(result *[]*model.User, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.User) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IUserDo
Assign(attrs ...field.AssignExpr) IUserDo
Joins(fields ...field.RelationField) IUserDo
Preload(fields ...field.RelationField) IUserDo
FirstOrInit() (*model.User, error)
FirstOrCreate() (*model.User, error)
FindByPage(offset int, limit int) (result []*model.User, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IUserDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (u userDo) Debug() IUserDo {
return u.withDO(u.DO.Debug())
}
func (u userDo) WithContext(ctx context.Context) IUserDo {
return u.withDO(u.DO.WithContext(ctx))
}
func (u userDo) ReadDB() IUserDo {
return u.Clauses(dbresolver.Read)
}
func (u userDo) WriteDB() IUserDo {
return u.Clauses(dbresolver.Write)
}
func (u userDo) Session(config *gorm.Session) IUserDo {
return u.withDO(u.DO.Session(config))
}
func (u userDo) Clauses(conds ...clause.Expression) IUserDo {
return u.withDO(u.DO.Clauses(conds...))
}
func (u userDo) Returning(value interface{}, columns ...string) IUserDo {
return u.withDO(u.DO.Returning(value, columns...))
}
func (u userDo) Not(conds ...gen.Condition) IUserDo {
return u.withDO(u.DO.Not(conds...))
}
func (u userDo) Or(conds ...gen.Condition) IUserDo {
return u.withDO(u.DO.Or(conds...))
}
func (u userDo) Select(conds ...field.Expr) IUserDo {
return u.withDO(u.DO.Select(conds...))
}
func (u userDo) Where(conds ...gen.Condition) IUserDo {
return u.withDO(u.DO.Where(conds...))
}
func (u userDo) Order(conds ...field.Expr) IUserDo {
return u.withDO(u.DO.Order(conds...))
}
func (u userDo) Distinct(cols ...field.Expr) IUserDo {
return u.withDO(u.DO.Distinct(cols...))
}
func (u userDo) Omit(cols ...field.Expr) IUserDo {
return u.withDO(u.DO.Omit(cols...))
}
func (u userDo) Join(table schema.Tabler, on ...field.Expr) IUserDo {
return u.withDO(u.DO.Join(table, on...))
}
func (u userDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserDo {
return u.withDO(u.DO.LeftJoin(table, on...))
}
func (u userDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserDo {
return u.withDO(u.DO.RightJoin(table, on...))
}
func (u userDo) Group(cols ...field.Expr) IUserDo {
return u.withDO(u.DO.Group(cols...))
}
func (u userDo) Having(conds ...gen.Condition) IUserDo {
return u.withDO(u.DO.Having(conds...))
}
func (u userDo) Limit(limit int) IUserDo {
return u.withDO(u.DO.Limit(limit))
}
func (u userDo) Offset(offset int) IUserDo {
return u.withDO(u.DO.Offset(offset))
}
func (u userDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserDo {
return u.withDO(u.DO.Scopes(funcs...))
}
func (u userDo) Unscoped() IUserDo {
return u.withDO(u.DO.Unscoped())
}
func (u userDo) Create(values ...*model.User) error {
if len(values) == 0 {
return nil
}
return u.DO.Create(values)
}
func (u userDo) CreateInBatches(values []*model.User, batchSize int) error {
return u.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (u userDo) Save(values ...*model.User) error {
if len(values) == 0 {
return nil
}
return u.DO.Save(values)
}
func (u userDo) First() (*model.User, error) {
if result, err := u.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.User), nil
}
}
func (u userDo) Take() (*model.User, error) {
if result, err := u.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.User), nil
}
}
func (u userDo) Last() (*model.User, error) {
if result, err := u.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.User), nil
}
}
func (u userDo) Find() ([]*model.User, error) {
result, err := u.DO.Find()
return result.([]*model.User), err
}
func (u userDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.User, err error) {
buf := make([]*model.User, 0, batchSize)
err = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (u userDo) FindInBatches(result *[]*model.User, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return u.DO.FindInBatches(result, batchSize, fc)
}
func (u userDo) Attrs(attrs ...field.AssignExpr) IUserDo {
return u.withDO(u.DO.Attrs(attrs...))
}
func (u userDo) Assign(attrs ...field.AssignExpr) IUserDo {
return u.withDO(u.DO.Assign(attrs...))
}
func (u userDo) Joins(fields ...field.RelationField) IUserDo {
for _, _f := range fields {
u = *u.withDO(u.DO.Joins(_f))
}
return &u
}
func (u userDo) Preload(fields ...field.RelationField) IUserDo {
for _, _f := range fields {
u = *u.withDO(u.DO.Preload(_f))
}
return &u
}
func (u userDo) FirstOrInit() (*model.User, error) {
if result, err := u.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.User), nil
}
}
func (u userDo) FirstOrCreate() (*model.User, error) {
if result, err := u.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.User), nil
}
}
func (u userDo) FindByPage(offset int, limit int) (result []*model.User, count int64, err error) {
result, err = u.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = u.Offset(-1).Limit(-1).Count()
return
}
func (u userDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = u.Count()
if err != nil {
return
}
err = u.Offset(offset).Limit(limit).Scan(result)
return
}
func (u userDo) Scan(result interface{}) (err error) {
return u.DO.Scan(result)
}
func (u userDo) Delete(models ...*model.User) (result gen.ResultInfo, err error) {
return u.DO.Delete(models)
}
func (u *userDo) withDO(do gen.Dao) *userDo {
u.DO = *do.(*gen.DO)
return u
}
================================================
FILE: dal/query/user_apply.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newUserApply(db *gorm.DB, opts ...gen.DOOption) userApply {
_userApply := userApply{}
_userApply.userApplyDo.UseDB(db, opts...)
_userApply.userApplyDo.UseModel(&model.UserApply{})
tableName := _userApply.userApplyDo.TableName()
_userApply.ALL = field.NewAsterisk(tableName)
_userApply.ID = field.NewInt64(tableName, "id")
_userApply.UID = field.NewInt64(tableName, "uid")
_userApply.Type = field.NewInt32(tableName, "type")
_userApply.TargetID = field.NewInt64(tableName, "target_id")
_userApply.Msg = field.NewString(tableName, "msg")
_userApply.Status = field.NewInt32(tableName, "status")
_userApply.ReadStatus = field.NewInt32(tableName, "read_status")
_userApply.CreateTime = field.NewTime(tableName, "create_time")
_userApply.UpdateTime = field.NewTime(tableName, "update_time")
_userApply.fillFieldMap()
return _userApply
}
// userApply 用户申请表
type userApply struct {
userApplyDo userApplyDo
ALL field.Asterisk
ID field.Int64 // id
UID field.Int64 // 申请人uid
Type field.Int32 // 申请类型 1加好友
TargetID field.Int64 // 接收人uid
Msg field.String // 申请信息
Status field.Int32 // 申请状态 1待审批 2同意
ReadStatus field.Int32 // 阅读状态 1未读 2已读
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (u userApply) Table(newTableName string) *userApply {
u.userApplyDo.UseTable(newTableName)
return u.updateTableName(newTableName)
}
func (u userApply) As(alias string) *userApply {
u.userApplyDo.DO = *(u.userApplyDo.As(alias).(*gen.DO))
return u.updateTableName(alias)
}
func (u *userApply) updateTableName(table string) *userApply {
u.ALL = field.NewAsterisk(table)
u.ID = field.NewInt64(table, "id")
u.UID = field.NewInt64(table, "uid")
u.Type = field.NewInt32(table, "type")
u.TargetID = field.NewInt64(table, "target_id")
u.Msg = field.NewString(table, "msg")
u.Status = field.NewInt32(table, "status")
u.ReadStatus = field.NewInt32(table, "read_status")
u.CreateTime = field.NewTime(table, "create_time")
u.UpdateTime = field.NewTime(table, "update_time")
u.fillFieldMap()
return u
}
func (u *userApply) WithContext(ctx context.Context) IUserApplyDo {
return u.userApplyDo.WithContext(ctx)
}
func (u userApply) TableName() string { return u.userApplyDo.TableName() }
func (u userApply) Alias() string { return u.userApplyDo.Alias() }
func (u userApply) Columns(cols ...field.Expr) gen.Columns { return u.userApplyDo.Columns(cols...) }
func (u *userApply) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := u.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (u *userApply) fillFieldMap() {
u.fieldMap = make(map[string]field.Expr, 9)
u.fieldMap["id"] = u.ID
u.fieldMap["uid"] = u.UID
u.fieldMap["type"] = u.Type
u.fieldMap["target_id"] = u.TargetID
u.fieldMap["msg"] = u.Msg
u.fieldMap["status"] = u.Status
u.fieldMap["read_status"] = u.ReadStatus
u.fieldMap["create_time"] = u.CreateTime
u.fieldMap["update_time"] = u.UpdateTime
}
func (u userApply) clone(db *gorm.DB) userApply {
u.userApplyDo.ReplaceConnPool(db.Statement.ConnPool)
return u
}
func (u userApply) replaceDB(db *gorm.DB) userApply {
u.userApplyDo.ReplaceDB(db)
return u
}
type userApplyDo struct{ gen.DO }
type IUserApplyDo interface {
gen.SubQuery
Debug() IUserApplyDo
WithContext(ctx context.Context) IUserApplyDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IUserApplyDo
WriteDB() IUserApplyDo
As(alias string) gen.Dao
Session(config *gorm.Session) IUserApplyDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IUserApplyDo
Not(conds ...gen.Condition) IUserApplyDo
Or(conds ...gen.Condition) IUserApplyDo
Select(conds ...field.Expr) IUserApplyDo
Where(conds ...gen.Condition) IUserApplyDo
Order(conds ...field.Expr) IUserApplyDo
Distinct(cols ...field.Expr) IUserApplyDo
Omit(cols ...field.Expr) IUserApplyDo
Join(table schema.Tabler, on ...field.Expr) IUserApplyDo
LeftJoin(table schema.Tabler, on ...field.Expr) IUserApplyDo
RightJoin(table schema.Tabler, on ...field.Expr) IUserApplyDo
Group(cols ...field.Expr) IUserApplyDo
Having(conds ...gen.Condition) IUserApplyDo
Limit(limit int) IUserApplyDo
Offset(offset int) IUserApplyDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IUserApplyDo
Unscoped() IUserApplyDo
Create(values ...*model.UserApply) error
CreateInBatches(values []*model.UserApply, batchSize int) error
Save(values ...*model.UserApply) error
First() (*model.UserApply, error)
Take() (*model.UserApply, error)
Last() (*model.UserApply, error)
Find() ([]*model.UserApply, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserApply, err error)
FindInBatches(result *[]*model.UserApply, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.UserApply) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IUserApplyDo
Assign(attrs ...field.AssignExpr) IUserApplyDo
Joins(fields ...field.RelationField) IUserApplyDo
Preload(fields ...field.RelationField) IUserApplyDo
FirstOrInit() (*model.UserApply, error)
FirstOrCreate() (*model.UserApply, error)
FindByPage(offset int, limit int) (result []*model.UserApply, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IUserApplyDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (u userApplyDo) Debug() IUserApplyDo {
return u.withDO(u.DO.Debug())
}
func (u userApplyDo) WithContext(ctx context.Context) IUserApplyDo {
return u.withDO(u.DO.WithContext(ctx))
}
func (u userApplyDo) ReadDB() IUserApplyDo {
return u.Clauses(dbresolver.Read)
}
func (u userApplyDo) WriteDB() IUserApplyDo {
return u.Clauses(dbresolver.Write)
}
func (u userApplyDo) Session(config *gorm.Session) IUserApplyDo {
return u.withDO(u.DO.Session(config))
}
func (u userApplyDo) Clauses(conds ...clause.Expression) IUserApplyDo {
return u.withDO(u.DO.Clauses(conds...))
}
func (u userApplyDo) Returning(value interface{}, columns ...string) IUserApplyDo {
return u.withDO(u.DO.Returning(value, columns...))
}
func (u userApplyDo) Not(conds ...gen.Condition) IUserApplyDo {
return u.withDO(u.DO.Not(conds...))
}
func (u userApplyDo) Or(conds ...gen.Condition) IUserApplyDo {
return u.withDO(u.DO.Or(conds...))
}
func (u userApplyDo) Select(conds ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.Select(conds...))
}
func (u userApplyDo) Where(conds ...gen.Condition) IUserApplyDo {
return u.withDO(u.DO.Where(conds...))
}
func (u userApplyDo) Order(conds ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.Order(conds...))
}
func (u userApplyDo) Distinct(cols ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.Distinct(cols...))
}
func (u userApplyDo) Omit(cols ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.Omit(cols...))
}
func (u userApplyDo) Join(table schema.Tabler, on ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.Join(table, on...))
}
func (u userApplyDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.LeftJoin(table, on...))
}
func (u userApplyDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.RightJoin(table, on...))
}
func (u userApplyDo) Group(cols ...field.Expr) IUserApplyDo {
return u.withDO(u.DO.Group(cols...))
}
func (u userApplyDo) Having(conds ...gen.Condition) IUserApplyDo {
return u.withDO(u.DO.Having(conds...))
}
func (u userApplyDo) Limit(limit int) IUserApplyDo {
return u.withDO(u.DO.Limit(limit))
}
func (u userApplyDo) Offset(offset int) IUserApplyDo {
return u.withDO(u.DO.Offset(offset))
}
func (u userApplyDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserApplyDo {
return u.withDO(u.DO.Scopes(funcs...))
}
func (u userApplyDo) Unscoped() IUserApplyDo {
return u.withDO(u.DO.Unscoped())
}
func (u userApplyDo) Create(values ...*model.UserApply) error {
if len(values) == 0 {
return nil
}
return u.DO.Create(values)
}
func (u userApplyDo) CreateInBatches(values []*model.UserApply, batchSize int) error {
return u.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (u userApplyDo) Save(values ...*model.UserApply) error {
if len(values) == 0 {
return nil
}
return u.DO.Save(values)
}
func (u userApplyDo) First() (*model.UserApply, error) {
if result, err := u.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.UserApply), nil
}
}
func (u userApplyDo) Take() (*model.UserApply, error) {
if result, err := u.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.UserApply), nil
}
}
func (u userApplyDo) Last() (*model.UserApply, error) {
if result, err := u.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.UserApply), nil
}
}
func (u userApplyDo) Find() ([]*model.UserApply, error) {
result, err := u.DO.Find()
return result.([]*model.UserApply), err
}
func (u userApplyDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserApply, err error) {
buf := make([]*model.UserApply, 0, batchSize)
err = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (u userApplyDo) FindInBatches(result *[]*model.UserApply, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return u.DO.FindInBatches(result, batchSize, fc)
}
func (u userApplyDo) Attrs(attrs ...field.AssignExpr) IUserApplyDo {
return u.withDO(u.DO.Attrs(attrs...))
}
func (u userApplyDo) Assign(attrs ...field.AssignExpr) IUserApplyDo {
return u.withDO(u.DO.Assign(attrs...))
}
func (u userApplyDo) Joins(fields ...field.RelationField) IUserApplyDo {
for _, _f := range fields {
u = *u.withDO(u.DO.Joins(_f))
}
return &u
}
func (u userApplyDo) Preload(fields ...field.RelationField) IUserApplyDo {
for _, _f := range fields {
u = *u.withDO(u.DO.Preload(_f))
}
return &u
}
func (u userApplyDo) FirstOrInit() (*model.UserApply, error) {
if result, err := u.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.UserApply), nil
}
}
func (u userApplyDo) FirstOrCreate() (*model.UserApply, error) {
if result, err := u.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.UserApply), nil
}
}
func (u userApplyDo) FindByPage(offset int, limit int) (result []*model.UserApply, count int64, err error) {
result, err = u.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = u.Offset(-1).Limit(-1).Count()
return
}
func (u userApplyDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = u.Count()
if err != nil {
return
}
err = u.Offset(offset).Limit(limit).Scan(result)
return
}
func (u userApplyDo) Scan(result interface{}) (err error) {
return u.DO.Scan(result)
}
func (u userApplyDo) Delete(models ...*model.UserApply) (result gen.ResultInfo, err error) {
return u.DO.Delete(models)
}
func (u *userApplyDo) withDO(do gen.Dao) *userApplyDo {
u.DO = *do.(*gen.DO)
return u
}
================================================
FILE: dal/query/user_friend.gen.go
================================================
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"DiTing-Go/dal/model"
)
func newUserFriend(db *gorm.DB, opts ...gen.DOOption) userFriend {
_userFriend := userFriend{}
_userFriend.userFriendDo.UseDB(db, opts...)
_userFriend.userFriendDo.UseModel(&model.UserFriend{})
tableName := _userFriend.userFriendDo.TableName()
_userFriend.ALL = field.NewAsterisk(tableName)
_userFriend.ID = field.NewInt64(tableName, "id")
_userFriend.UID = field.NewInt64(tableName, "uid")
_userFriend.FriendUID = field.NewInt64(tableName, "friend_uid")
_userFriend.DeleteStatus = field.NewInt32(tableName, "delete_status")
_userFriend.CreateTime = field.NewTime(tableName, "create_time")
_userFriend.UpdateTime = field.NewTime(tableName, "update_time")
_userFriend.fillFieldMap()
return _userFriend
}
// userFriend 用户联系人表
type userFriend struct {
userFriendDo userFriendDo
ALL field.Asterisk
ID field.Int64 // id
UID field.Int64 // uid
FriendUID field.Int64 // 好友uid
DeleteStatus field.Int32 // 逻辑删除(0-正常,1-删除)
CreateTime field.Time // 创建时间
UpdateTime field.Time // 修改时间
fieldMap map[string]field.Expr
}
func (u userFriend) Table(newTableName string) *userFriend {
u.userFriendDo.UseTable(newTableName)
return u.updateTableName(newTableName)
}
func (u userFriend) As(alias string) *userFriend {
u.userFriendDo.DO = *(u.userFriendDo.As(alias).(*gen.DO))
return u.updateTableName(alias)
}
func (u *userFriend) updateTableName(table string) *userFriend {
u.ALL = field.NewAsterisk(table)
u.ID = field.NewInt64(table, "id")
u.UID = field.NewInt64(table, "uid")
u.FriendUID = field.NewInt64(table, "friend_uid")
u.DeleteStatus = field.NewInt32(table, "delete_status")
u.CreateTime = field.NewTime(table, "create_time")
u.UpdateTime = field.NewTime(table, "update_time")
u.fillFieldMap()
return u
}
func (u *userFriend) WithContext(ctx context.Context) IUserFriendDo {
return u.userFriendDo.WithContext(ctx)
}
func (u userFriend) TableName() string { return u.userFriendDo.TableName() }
func (u userFriend) Alias() string { return u.userFriendDo.Alias() }
func (u userFriend) Columns(cols ...field.Expr) gen.Columns { return u.userFriendDo.Columns(cols...) }
func (u *userFriend) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := u.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (u *userFriend) fillFieldMap() {
u.fieldMap = make(map[string]field.Expr, 6)
u.fieldMap["id"] = u.ID
u.fieldMap["uid"] = u.UID
u.fieldMap["friend_uid"] = u.FriendUID
u.fieldMap["delete_status"] = u.DeleteStatus
u.fieldMap["create_time"] = u.CreateTime
u.fieldMap["update_time"] = u.UpdateTime
}
func (u userFriend) clone(db *gorm.DB) userFriend {
u.userFriendDo.ReplaceConnPool(db.Statement.ConnPool)
return u
}
func (u userFriend) replaceDB(db *gorm.DB) userFriend {
u.userFriendDo.ReplaceDB(db)
return u
}
type userFriendDo struct{ gen.DO }
type IUserFriendDo interface {
gen.SubQuery
Debug() IUserFriendDo
WithContext(ctx context.Context) IUserFriendDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IUserFriendDo
WriteDB() IUserFriendDo
As(alias string) gen.Dao
Session(config *gorm.Session) IUserFriendDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IUserFriendDo
Not(conds ...gen.Condition) IUserFriendDo
Or(conds ...gen.Condition) IUserFriendDo
Select(conds ...field.Expr) IUserFriendDo
Where(conds ...gen.Condition) IUserFriendDo
Order(conds ...field.Expr) IUserFriendDo
Distinct(cols ...field.Expr) IUserFriendDo
Omit(cols ...field.Expr) IUserFriendDo
Join(table schema.Tabler, on ...field.Expr) IUserFriendDo
LeftJoin(table schema.Tabler, on ...field.Expr) IUserFriendDo
RightJoin(table schema.Tabler, on ...field.Expr) IUserFriendDo
Group(cols ...field.Expr) IUserFriendDo
Having(conds ...gen.Condition) IUserFriendDo
Limit(limit int) IUserFriendDo
Offset(offset int) IUserFriendDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IUserFriendDo
Unscoped() IUserFriendDo
Create(values ...*model.UserFriend) error
CreateInBatches(values []*model.UserFriend, batchSize int) error
Save(values ...*model.UserFriend) error
First() (*model.UserFriend, error)
Take() (*model.UserFriend, error)
Last() (*model.UserFriend, error)
Find() ([]*model.UserFriend, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserFriend, err error)
FindInBatches(result *[]*model.UserFriend, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.UserFriend) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IUserFriendDo
Assign(attrs ...field.AssignExpr) IUserFriendDo
Joins(fields ...field.RelationField) IUserFriendDo
Preload(fields ...field.RelationField) IUserFriendDo
FirstOrInit() (*model.UserFriend, error)
FirstOrCreate() (*model.UserFriend, error)
FindByPage(offset int, limit int) (result []*model.UserFriend, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IUserFriendDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (u userFriendDo) Debug() IUserFriendDo {
return u.withDO(u.DO.Debug())
}
func (u userFriendDo) WithContext(ctx context.Context) IUserFriendDo {
return u.withDO(u.DO.WithContext(ctx))
}
func (u userFriendDo) ReadDB() IUserFriendDo {
return u.Clauses(dbresolver.Read)
}
func (u userFriendDo) WriteDB() IUserFriendDo {
return u.Clauses(dbresolver.Write)
}
func (u userFriendDo) Session(config *gorm.Session) IUserFriendDo {
return u.withDO(u.DO.Session(config))
}
func (u userFriendDo) Clauses(conds ...clause.Expression) IUserFriendDo {
return u.withDO(u.DO.Clauses(conds...))
}
func (u userFriendDo) Returning(value interface{}, columns ...string) IUserFriendDo {
return u.withDO(u.DO.Returning(value, columns...))
}
func (u userFriendDo) Not(conds ...gen.Condition) IUserFriendDo {
return u.withDO(u.DO.Not(conds...))
}
func (u userFriendDo) Or(conds ...gen.Condition) IUserFriendDo {
return u.withDO(u.DO.Or(conds...))
}
func (u userFriendDo) Select(conds ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.Select(conds...))
}
func (u userFriendDo) Where(conds ...gen.Condition) IUserFriendDo {
return u.withDO(u.DO.Where(conds...))
}
func (u userFriendDo) Order(conds ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.Order(conds...))
}
func (u userFriendDo) Distinct(cols ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.Distinct(cols...))
}
func (u userFriendDo) Omit(cols ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.Omit(cols...))
}
func (u userFriendDo) Join(table schema.Tabler, on ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.Join(table, on...))
}
func (u userFriendDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.LeftJoin(table, on...))
}
func (u userFriendDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.RightJoin(table, on...))
}
func (u userFriendDo) Group(cols ...field.Expr) IUserFriendDo {
return u.withDO(u.DO.Group(cols...))
}
func (u userFriendDo) Having(conds ...gen.Condition) IUserFriendDo {
return u.withDO(u.DO.Having(conds...))
}
func (u userFriendDo) Limit(limit int) IUserFriendDo {
return u.withDO(u.DO.Limit(limit))
}
func (u userFriendDo) Offset(offset int) IUserFriendDo {
return u.withDO(u.DO.Offset(offset))
}
func (u userFriendDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserFriendDo {
return u.withDO(u.DO.Scopes(funcs...))
}
func (u userFriendDo) Unscoped() IUserFriendDo {
return u.withDO(u.DO.Unscoped())
}
func (u userFriendDo) Create(values ...*model.UserFriend) error {
if len(values) == 0 {
return nil
}
return u.DO.Create(values)
}
func (u userFriendDo) CreateInBatches(values []*model.UserFriend, batchSize int) error {
return u.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (u userFriendDo) Save(values ...*model.UserFriend) error {
if len(values) == 0 {
return nil
}
return u.DO.Save(values)
}
func (u userFriendDo) First() (*model.UserFriend, error) {
if result, err := u.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.UserFriend), nil
}
}
func (u userFriendDo) Take() (*model.UserFriend, error) {
if result, err := u.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.UserFriend), nil
}
}
func (u userFriendDo) Last() (*model.UserFriend, error) {
if result, err := u.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.UserFriend), nil
}
}
func (u userFriendDo) Find() ([]*model.UserFriend, error) {
result, err := u.DO.Find()
return result.([]*model.UserFriend), err
}
func (u userFriendDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserFriend, err error) {
buf := make([]*model.UserFriend, 0, batchSize)
err = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (u userFriendDo) FindInBatches(result *[]*model.UserFriend, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return u.DO.FindInBatches(result, batchSize, fc)
}
func (u userFriendDo) Attrs(attrs ...field.AssignExpr) IUserFriendDo {
return u.withDO(u.DO.Attrs(attrs...))
}
func (u userFriendDo) Assign(attrs ...field.AssignExpr) IUserFriendDo {
return u.withDO(u.DO.Assign(attrs...))
}
func (u userFriendDo) Joins(fields ...field.RelationField) IUserFriendDo {
for _, _f := range fields {
u = *u.withDO(u.DO.Joins(_f))
}
return &u
}
func (u userFriendDo) Preload(fields ...field.RelationField) IUserFriendDo {
for _, _f := range fields {
u = *u.withDO(u.DO.Preload(_f))
}
return &u
}
func (u userFriendDo) FirstOrInit() (*model.UserFriend, error) {
if result, err := u.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.UserFriend), nil
}
}
func (u userFriendDo) FirstOrCreate() (*model.UserFriend, error) {
if result, err := u.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.UserFriend), nil
}
}
func (u userFriendDo) FindByPage(offset int, limit int) (result []*model.UserFriend, count int64, err error) {
result, err = u.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = u.Offset(-1).Limit(-1).Count()
return
}
func (u userFriendDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = u.Count()
if err != nil {
return
}
err = u.Offset(offset).Limit(limit).Scan(result)
return
}
func (u userFriendDo) Scan(result interface{}) (err error) {
return u.DO.Scan(result)
}
func (u userFriendDo) Delete(models ...*model.UserFriend) (result gen.ResultInfo, err error) {
return u.DO.Delete(models)
}
func (u *userFriendDo) withDO(do gen.Dao) *userFriendDo {
u.DO = *do.(*gen.DO)
return u
}
================================================
FILE: docs/docs.go
================================================
// Code generated by swaggo/swag. DO NOT EDIT.
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/api/contact/add": {
"post": {
"produces": [
"application/json"
],
"summary": "添加好友",
"parameters": [
{
"description": "好友uid",
"name": "uid",
"in": "body",
"required": true,
"schema": {
"type": "integer"
}
},
{
"description": "验证消息",
"name": "msg",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/contact/delete": {
"put": {
"produces": [
"application/json"
],
"summary": "同意好友申请",
"parameters": [
{
"description": "好友uid",
"name": "uid",
"in": "body",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
},
"delete": {
"produces": [
"application/json"
],
"summary": "删除好友",
"parameters": [
{
"description": "好友uid",
"name": "uid",
"in": "body",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/contact/getApplyList": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"produces": [
"application/json"
],
"summary": "获取用户好友申请列表",
"parameters": [
{
"type": "integer",
"description": "last_id",
"name": "last_id",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "limit",
"name": "limit",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/public/login": {
"post": {
"produces": [
"application/json"
],
"summary": "用户登录",
"parameters": [
{
"description": "用户名",
"name": "name",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "密码",
"name": "password",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/public/register": {
"post": {
"produces": [
"application/json"
],
"summary": "用户注册",
"parameters": [
{
"description": "用户名",
"name": "name",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "密码",
"name": "password",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
}
},
"definitions": {
"resp.ResponseData": {
"type": "object",
"properties": {
"code": {
"description": "状态码",
"type": "integer"
},
"data": {
"description": "响应数据"
},
"message": {
"description": "响应消息",
"type": "string"
}
}
}
},
"securityDefinitions": {
"ApiKeyAuth": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}
================================================
FILE: docs/swagger.json
================================================
{
"swagger": "2.0",
"info": {
"contact": {}
},
"paths": {
"/api/contact/add": {
"post": {
"produces": [
"application/json"
],
"summary": "添加好友",
"parameters": [
{
"description": "好友uid",
"name": "uid",
"in": "body",
"required": true,
"schema": {
"type": "integer"
}
},
{
"description": "验证消息",
"name": "msg",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/contact/delete": {
"put": {
"produces": [
"application/json"
],
"summary": "同意好友申请",
"parameters": [
{
"description": "好友uid",
"name": "uid",
"in": "body",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
},
"delete": {
"produces": [
"application/json"
],
"summary": "删除好友",
"parameters": [
{
"description": "好友uid",
"name": "uid",
"in": "body",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/contact/getApplyList": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"produces": [
"application/json"
],
"summary": "获取用户好友申请列表",
"parameters": [
{
"type": "integer",
"description": "last_id",
"name": "last_id",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "limit",
"name": "limit",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/public/login": {
"post": {
"produces": [
"application/json"
],
"summary": "用户登录",
"parameters": [
{
"description": "用户名",
"name": "name",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "密码",
"name": "password",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
},
"/api/public/register": {
"post": {
"produces": [
"application/json"
],
"summary": "用户注册",
"parameters": [
{
"description": "用户名",
"name": "name",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "密码",
"name": "password",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
},
"500": {
"description": "内部错误",
"schema": {
"$ref": "#/definitions/resp.ResponseData"
}
}
}
}
}
},
"definitions": {
"resp.ResponseData": {
"type": "object",
"properties": {
"code": {
"description": "状态码",
"type": "integer"
},
"data": {
"description": "响应数据"
},
"message": {
"description": "响应消息",
"type": "string"
}
}
}
},
"securityDefinitions": {
"ApiKeyAuth": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
}
}
================================================
FILE: domain/dto/contact_dto.go
================================================
package dto
type ContactDto struct {
// 会话ID
ID int64 `json:"id"`
// 房间ID
RoomID int64 `json:"roomId"`
// 头像
Avatar string `json:"avatar"`
// 会话名称
Name string `json:"name"`
// 最后一条消息内容
LastMsg string `json:"lastMsg"`
// 最后一条消息时间 时间戳格式
LastTime int64 `json:"lastTime"`
// 未读消息数
UnreadCount int32 `json:"unreadCount"`
// 会话类型
Type int `json:"type"`
}
================================================
FILE: domain/dto/delete_friend_dto.go
================================================
package dto
type DeleteFriendDto struct {
Uid int64 `json:"uid"`
FriendUid int64 `json:"friend_uid"`
}
================================================
FILE: domain/dto/group_dto.go
================================================
package dto
import "time"
type GetGroupMemberDto struct {
// 用户ID
UID int64 `json:"uid" gorm:"column:id"`
// 用户名
Name string `json:"name"`
// 头像
Avatar string `json:"avatar"`
// 用户状态
ActiveStatus int32 `json:"activeStatus"`
// 最后活跃时间
LastOptTime time.Time `json:"lastOptTime"`
}
================================================
FILE: domain/dto/msg_dto.go
================================================
package dto
type MessageBaseDto struct {
// 下载地址
Url string `json:"url"`
// 文件大小
Size int64 `json:"size"`
// 文件名
Name string `json:"name"`
}
type ImgMessageDto struct {
MessageBaseDto MessageBaseDto `json:"message_base_dto"`
// 图片高度
Height int `json:"height"`
// 图片宽度
Width int `json:"width"`
}
================================================
FILE: domain/enum/event_bus.go
================================================
package enum
const (
FriendApplyEvent = "main:FriendApplyEvent"
FriendNewEvent = "main:FriendNewEvent"
NewMessageEvent = "main:NewMessageEvent"
)
================================================
FILE: domain/enum/login.go
================================================
package enum
const (
LoginByPassword = 1 // 用户名密码登录
LoginByPhoneCaptcha = 2 // 手机号验证码登录
)
================================================
FILE: domain/enum/message.go
================================================
package enum
const (
TextMessage = 1
)
const (
TextMessageType = 1
ImgMessageType = 3
)
================================================
FILE: domain/enum/redis.go
================================================
package enum
import "time"
const (
Project = "diting:"
User = Project + "User"
UserFriend = Project + "userFriend:"
UserApply = Project + "userApply:"
RoomFriend = Project + "roomFriend:"
Contact = Project + "contact:"
Room = Project + "room:"
)
const (
// 房间缓存
RoomCacheByID = Room + "%d"
// 好友房间缓存
RoomFriendCacheByRoomID = RoomFriend + "%d"
RoomFriendCacheByUidAndFriendUid = RoomFriend + "%d_%d"
// phoneUid映射
PhoneUidMap = User + "PhoneUid:" + "%s"
UserCacheByID = User + "%d"
UserCacheByName = User + "%s"
UserCacheByPhone = User + "Phone:" + "%s"
UserCaptcha = User + "Captcha:" + "%s"
// 用户好友缓存
UserFriendCacheByUidAndFriendUid = UserFriend + "%d_%d"
// 好友申请缓存
UserApplyCacheByUidAndFriendUid = UserApply + "%d_%d"
// 会话缓存
ContactCacheById = Contact + "%d"
)
const (
DefaultCacheTime = 7 * 24 * time.Hour
NotExpireTime = 10 * 365 * 24 * time.Hour
)
================================================
FILE: domain/enum/redsync.go
================================================
package enum
const (
Lock = "lock:"
UserLock = Lock + "diting-user:"
UserAndFriendLock = UserLock + "%d_%d"
)
================================================
FILE: domain/enum/rocketmq.go
================================================
package enum
const (
UserLoginTopic = "diting-login"
NewFriendTopic = "diting-new-friend"
NewMessageTopic = "diting-new-message"
DeleteFriendTopic = "diting-delete-friend"
FriendApplyTopic = "diting-friend-apply"
)
================================================
FILE: domain/enum/room.go
================================================
package enum
const (
// 房间状态
HOT = 1
NORMAL = 0
// 房间类型
GROUP = 1
PERSONAL = 2
)
================================================
FILE: domain/enum/user.go
================================================
package enum
const (
UserStatusNormal = 1
UserStatusCancel = 2
)
================================================
FILE: domain/model/message.go
================================================
package model
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/enum"
)
type Message model.Message
func (msg Message) GetContactMsg() string {
if msg.Type == enum.TextMessageType {
return msg.Content
} else if msg.Type == enum.ImgMessageType {
return "[图片]"
}
return msg.Content
}
================================================
FILE: domain/vo/req/agree_friend_req.go
================================================
package req
type AgreeFriendReq struct {
Uid int64 `json:"uid" binding:"required"`
}
================================================
FILE: domain/vo/req/captcha_req.go
================================================
package req
type CaptchaReq struct {
// 手机号
Phone string `json:"phone" binding:"required"`
}
================================================
FILE: domain/vo/req/create_group_req.go
================================================
package req
type CreateGroupReq struct {
UidList []int64 `json:"uidList" binding:"required"`
}
================================================
FILE: domain/vo/req/delete_friend_req.go
================================================
package req
type DeleteFriendReq struct {
Uid int64 `json:"uid" binding:"required"`
}
================================================
FILE: domain/vo/req/delete_group_req.go
================================================
package req
type DeleteGroupReq struct {
ID int64 `uri:"id" binding:"required"`
}
================================================
FILE: domain/vo/req/get_group_member_list_req.go
================================================
package req
type GetGroupMemberListReq struct {
// 房间ID
RoomId int64 `form:"roomId" binding:"required"`
Cursor *string `form:"cursor"`
PageSize int `form:"pageSize" binding:"required"`
}
================================================
FILE: domain/vo/req/get_message_list_req.go
================================================
package req
type GetMessageListReq struct {
RoomId int64 `json:"roomId" form:"roomId" binding:"required"`
Cursor *string `json:"cursor" form:"cursor" binding:"required"`
PageSize int `json:"pageSize" form:"pageSize" binding:"required"`
}
================================================
FILE: domain/vo/req/get_new_content_list_req.go
================================================
package req
type GetNewContentListReq struct {
Timestamp int64 `json:"timestamp" form:"timestamp" binding:"required"`
}
================================================
FILE: domain/vo/req/get_new_msg_list_req.go
================================================
package req
type GetNewMsgListReq struct {
// 房间ID
RoomId int64 `json:"roomId" form:"roomId" binding:"required"`
// 消息ID
MsgId int64 `json:"msgId" form:"msgId" binding:"required"`
}
================================================
FILE: domain/vo/req/get_user_info_batch_req.go
================================================
package req
type UserInfoBatchReqItem struct {
Uid int64 `json:"uid"`
LastModifyTime int64 `json:"lastModifyTime"`
}
type GetUserInfoBatchReq struct {
List []UserInfoBatchReqItem `json:"list" bind:"required"`
}
================================================
FILE: domain/vo/req/get_user_info_by_name_req.go
================================================
package req
type GetUserInfoByNameReq struct {
// 用户名
Name string `form:"name" binding:"required"`
}
================================================
FILE: domain/vo/req/grant_administrator_req.go
================================================
package req
type GrantAdministratorReq struct {
// 房间ID
RoomId int64 `json:"room_id" binding:"required"`
// 授权用户ID
GrantUid int64 `json:"grant_uid" binding:"required"`
}
================================================
FILE: domain/vo/req/is_friend_req.go
================================================
package req
// IsFriendReq 是否是好友请求
type IsFriendReq struct {
FriendUid int64 `uri:"friendUid" binding:"required"`
}
================================================
FILE: domain/vo/req/join_group_req.go
================================================
package req
type JoinGroupReq struct {
ID int64 `json:"id" binding:"required"`
}
================================================
FILE: domain/vo/req/message_req.go
================================================
package req
type MessageBody struct {
Content string `json:"content" form:"content" binding:"required"`
ReplyMsgId int64 `json:"replyMsgId" form:"replyMsgId"`
}
type MessageReq struct {
RoomId int64 `json:"roomId" form:"roomId" binding:"required"`
MsgType int32 `json:"msgType" form:"msgType" binding:"required"`
Body MessageBody `json:"body" form:"body" binding:"required"`
}
================================================
FILE: domain/vo/req/quit_group_req.go
================================================
package req
type QuitGroupReq struct {
// 房间di
ID int64 `json:"id" binding:"required"`
}
================================================
FILE: domain/vo/req/remove_administrator_req.go
================================================
package req
type RemoveAdministratorReq struct {
// 房间ID
RoomId int64 `json:"room_id" binding:"required"`
// 移除用户ID
RemoveUid int64 `json:"remove_uid" binding:"required"`
}
================================================
FILE: domain/vo/req/uid_req.go
================================================
package req
type UidReq struct {
Uid int64 `json:"uid"`
}
================================================
FILE: domain/vo/req/user_apply_req.go
================================================
package req
type UserApplyReq struct {
Uid int64 `json:"uid"`
Msg string `json:"msg"`
}
================================================
FILE: domain/vo/req/user_cancle_req.go
================================================
package req
type UserCancelReq struct {
Captcha string `json:"captcha" binding:"required"`
}
================================================
FILE: domain/vo/req/user_login_req.go
================================================
package req
type UserLoginReq struct {
UserName string `json:"username,omitempty"` // 用户名可选
Password string `json:"password,omitempty"` // 密码可选
Phone string `json:"phone,omitempty"` // 手机号可选
Captcha string `json:"captcha,omitempty"` // 验证码
LoginType int `json:"loginType" binding:"required"` // 登录类型, 1: 用户名密码登录, 2: 手机号验证码登录
}
================================================
FILE: domain/vo/req/user_register_req.go
================================================
package req
type UserRegisterReq struct {
// 用户名
Username string `json:"username" binding:"required"`
// 密码
Password string `json:"password" binding:"required"`
// 手机号
Phone string `json:"phone" binding:"required"`
// 验证码
Captcha string `json:"captcha" binding:"required"` // 如果需要验证码,可以取消注释
}
================================================
FILE: domain/vo/resp/get_user_info_batch_resp.go
================================================
package resp
type GetUserInfoBatchResp struct {
Uid int64 `json:"uid"`
Username string `json:"name"`
Avatar string `json:"avatar"`
NeedRefresh bool `json:"needRefresh"`
}
================================================
FILE: domain/vo/resp/get_user_info_by_name_resp.go
================================================
package resp
type GetUserInfoByNameResp struct {
// 用户ID
Uid int64 `json:"uid"`
// 用户名
Name string `json:"name"`
// 头像
Avatar string `json:"avatar"`
// 好友状态
Status int32 `json:"status"`
}
================================================
FILE: domain/vo/resp/message_resp.go
================================================
package resp
type MsgUser struct {
Uid int64 `json:"uid"`
Username string `json:"username"`
Avatar string `json:"avatar"`
}
type Msg struct {
ID int64 `json:"id"`
RoomId int64 `json:"roomId"`
Type int32 `json:"type"`
Body TextBody `json:"body"`
}
type TextBody struct {
Content string `json:"content"`
Reply int64 `json:"reply"`
}
type MessageResp struct {
FromUser MsgUser `json:"fromUser"`
Message Msg `json:"message"`
SendTime int64 `json:"sendTime"`
}
================================================
FILE: domain/vo/resp/page_list_resp.go
================================================
package resp
type PageListResp struct {
List interface{} `json:"dataList"`
Total int `json:"total"`
}
================================================
FILE: domain/vo/resp/pre_signed_resp.go
================================================
package resp
type PreSignedResp struct {
Url string `json:"url"`
Policy map[string]string `json:"policy"`
}
================================================
FILE: domain/vo/resp/user_apply_resp.go
================================================
package resp
type UserApplyResp struct {
ApplyId int64 `json:"applyId"` // 申请ID
Uid int64 `json:"uid"` // 用户ID
Msg string `json:"msg"` // 申请信息
Status int32 `json:"status"` // 使用状态 1.待审批 2.已接受
}
================================================
FILE: domain/vo/resp/user_contact_resp.go
================================================
package resp
type UserContactResp struct {
Uid int64 `json:"uid"` // 用户ID
ActiveStatus int `json:"activeStatus"` // 用户状态
LastOptTime int64 `json:"lastOptTime"` // 最后操作时间
}
================================================
FILE: domain/vo/resp/user_login_resp.go
================================================
package resp
type UserLoginResp struct {
Token string `json:"token"`
Uid int64 `json:"uid"`
Name string `json:"name"`
Avatar string `json:"avatar"`
}
================================================
FILE: event/listener/friend_apply_event.go
================================================
package listener
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"DiTing-Go/utils/jsonUtils"
"context"
"github.com/apache/rocketmq-client-go/v2"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
"github.com/spf13/viper"
)
func init() {
host := viper.GetString("rocketmq.host")
// 设置推送消费者
rocketConsumer, _ := rocketmq.NewPushConsumer(
//消费组
consumer.WithGroupName(enum.FriendApplyTopic),
// namesrv地址
consumer.WithNameServer([]string{host}),
)
err := rocketConsumer.Subscribe(enum.FriendApplyTopic, consumer.MessageSelector{}, friendApplyEvent)
if err != nil {
global.Logger.Panicf("subscribe error: %s", err.Error())
}
err = rocketConsumer.Start()
if err != nil {
global.Logger.Panicf("start consumer error: %s", err.Error())
}
}
// FriendApplyEvent 好友申请事件
func friendApplyEvent(ctx context.Context, ext ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for i := range ext {
// 解码
userApplyR := model.UserApply{}
if err := jsonUtils.UnmarshalMsg(&userApplyR, ext[i]); err != nil {
global.Logger.Errorf("jsonUtils unmarshal error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
if err := friendApply(userApplyR); err != nil {
global.Logger.Errorf("friendApply error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
}
return consumer.ConsumeSuccess, nil
}
func friendApply(apply model.UserApply) error {
// 发送新消息事件
//service.Send(apply.TargetID)
return nil
}
================================================
FILE: event/listener/friend_delete_event.go
================================================
package listener
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/dto"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
pkgEnum "DiTing-Go/pkg/domain/enum"
"DiTing-Go/pkg/utils"
"DiTing-Go/utils/jsonUtils"
"DiTing-Go/utils/redisCache"
"context"
"fmt"
"github.com/apache/rocketmq-client-go/v2"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
"github.com/pkg/errors"
"github.com/spf13/viper"
"sort"
)
func init() {
host := viper.GetString("rocketmq.host")
// 设置推送消费者
rocketConsumer, _ := rocketmq.NewPushConsumer(
//消费组
consumer.WithGroupName(enum.DeleteFriendTopic),
// namesrv地址
consumer.WithNameServer([]string{host}),
)
err := rocketConsumer.Subscribe(enum.DeleteFriendTopic, consumer.MessageSelector{}, deleteFriendEvent)
if err != nil {
global.Logger.Panicf("subscribe error: %s", err.Error())
}
err = rocketConsumer.Start()
if err != nil {
global.Logger.Panicf("start consumer error: %s", err.Error())
}
}
func deleteFriendEvent(ctx context.Context, ext ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for i := range ext {
// 解码
deleteFriendDto := dto.DeleteFriendDto{}
if err := jsonUtils.UnmarshalMsg(&deleteFriendDto, ext[i]); err != nil {
global.Logger.Errorf("jsonUtils unmarshal error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
if err := deleteFriend(deleteFriendDto); err != nil {
global.Logger.Errorf("deleteFriend error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
}
return consumer.ConsumeSuccess, nil
}
func deleteFriend(deleteFriendDto dto.DeleteFriendDto) error {
uid := deleteFriendDto.Uid
deleteFriendUid := deleteFriendDto.FriendUid
// 加锁
uids := utils.Int64Slice{uid, deleteFriendUid}
sort.Sort(uids)
key := fmt.Sprintf(enum.UserAndFriendLock, uids[0], uids[1])
mutex, err := utils.GetLock(key)
if err != nil {
return err
}
defer utils.ReleaseLock(mutex)
ctx := context.Background()
tx := global.Query.Begin()
userApply := global.Query.UserApply
userApplyTx := tx.UserApply.WithContext(ctx)
// 删除好友申请
if _, err := userApplyTx.Where(userApply.UID.Eq(uid), userApply.TargetID.Eq(deleteFriendUid)).Delete(); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
}
global.Logger.Errorf("删除好友失败 %s", err.Error())
return errors.New("Business Error")
}
// 删除redis缓存
defer redisCache.RemoveUserApply(uid, deleteFriendUid)
if _, err := userApplyTx.Where(userApply.UID.Eq(deleteFriendUid), userApply.TargetID.Eq(uid)).Delete(); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
}
global.Logger.Errorf("删除好友失败 %s", err.Error())
return errors.New("Business Error")
}
// 删除redis缓存
defer redisCache.RemoveUserApply(deleteFriendUid, uid)
// 软删除好友房间
roomFriend := global.Query.RoomFriend
roomFriendTx := tx.RoomFriend.WithContext(ctx)
uids = utils.Int64Slice{uid, deleteFriendUid}
sort.Sort(uids)
fun := func() (interface{}, error) {
return roomFriendTx.Where(roomFriend.Uid1.Eq(uids[0]), roomFriend.Uid2.Eq(uids[1])).First()
}
roomFriendR := model.RoomFriend{}
key = fmt.Sprintf(enum.RoomFriendCacheByUidAndFriendUid, uids[0], uids[1])
if err := utils.GetData(key, &roomFriendR, fun); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
}
global.Logger.Errorf("查询好友房间失败 %s", err.Error())
return errors.New("Business Error")
}
if _, err := roomFriendTx.Where(roomFriend.ID.Eq(roomFriendR.ID)).Update(roomFriend.DeleteStatus, pkgEnum.DELETED); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
}
global.Logger.Errorf("删除好友房间失败 %s", err.Error())
return errors.New("Business Error")
}
// 删除redis缓存
defer redisCache.RemoveRoomFriend(roomFriendR)
// 软删除房间表
room := global.Query.Room
roomTx := tx.Room.WithContext(ctx)
if _, err := roomTx.Where(room.ID.Eq(roomFriendR.RoomID)).Update(room.DeleteStatus, pkgEnum.DELETED); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
}
global.Logger.Errorf("删除房间失败 %s", err.Error())
return errors.New("Business Error")
}
// 删除redis缓存
roomR := model.Room{
ID: roomFriendR.RoomID,
}
defer redisCache.RemoveRoomCache(roomR)
// 删除消息表
msg := global.Query.Message
msgTx := tx.Message.WithContext(ctx)
if _, err := msgTx.Where(msg.RoomID.Eq(roomFriendR.RoomID)).Update(msg.DeleteStatus, pkgEnum.DELETED); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
}
global.Logger.Errorf("删除消息失败 %s", err.Error())
return errors.New("Business Error")
}
// TODO: 删除消息表缓存
if err := tx.Commit(); err != nil {
global.Logger.Errorf("事务提交失败 %s", err.Error())
return errors.New("Business Error")
}
return nil
}
================================================
FILE: event/listener/friend_new_event.go
================================================
package listener
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
pkgEnum "DiTing-Go/pkg/domain/enum"
"DiTing-Go/pkg/utils"
"DiTing-Go/service"
"DiTing-Go/utils/redisCache"
"context"
"fmt"
"github.com/apache/rocketmq-client-go/v2"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
"github.com/goccy/go-json"
"github.com/pkg/errors"
"github.com/spf13/viper"
"gorm.io/gorm"
"sort"
"strconv"
"time"
)
func init() {
host := viper.GetString("rocketmq.host")
// 设置推送消费者
rocketConsumer, _ := rocketmq.NewPushConsumer(
//消费组
consumer.WithGroupName(enum.UserLoginTopic),
// namesrv地址
consumer.WithNameServer([]string{host}),
)
err := rocketConsumer.Subscribe(enum.NewFriendTopic, consumer.MessageSelector{}, friendNewEvent)
if err != nil {
global.Logger.Panicf("subscribe error: %s", err.Error())
}
err = rocketConsumer.Start()
if err != nil {
global.Logger.Panicf("start consumer error: %s", err.Error())
}
}
func friendNewEvent(ctx context.Context, ext ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for i := range ext {
// 解码
userFriend := model.UserFriend{}
userFriendMsgByte := ext[i].Message.Body
err := json.Unmarshal(userFriendMsgByte, &userFriend)
if err != nil {
global.Logger.Errorf("json unmarshal error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
err = friendNew(userFriend)
if err != nil {
global.Logger.Errorf("friendNew error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
}
return consumer.ConsumeSuccess, nil
}
func friendNew(userFriend model.UserFriend) error {
ctx := context.Background()
uid := userFriend.UID
friendUid := userFriend.FriendUID
uids := utils.Int64Slice{uid, friendUid}
sort.Sort(uids)
key := fmt.Sprintf(enum.UserAndFriendLock, uids[0], uids[1])
mutex, err := utils.GetLock(key)
if err != nil {
return err
}
defer utils.ReleaseLock(mutex)
q := global.Query
tx := q.Begin()
roomQ := tx.WithContext(ctx).Room
roomFriendQ := tx.WithContext(ctx).RoomFriend
contactQ := tx.WithContext(ctx).Contact
// 创建房间表
room := model.Room{
Type: enum.PERSONAL,
HotFlag: enum.NORMAL,
ExtJSON: "{}",
}
if err := roomQ.Create(&room); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
global.Logger.Errorf("创建房间失败 %s", err.Error())
return err
}
// 排序,uid小的在前
uids = utils.Int64Slice{userFriend.UID, userFriend.FriendUID}
sort.Sort(uids)
//检查是否有软删除状态的记录
roomFriend := global.Query.RoomFriend
fun := func() (interface{}, error) {
return roomFriendQ.Where(roomFriend.Uid1.Eq(uids[0]), roomFriend.Uid2.Eq(uids[1]), roomFriend.DeleteStatus.Eq(pkgEnum.DELETED)).First()
}
roomFriedR := model.RoomFriend{}
key = fmt.Sprintf(enum.RoomFriendCacheByUidAndFriendUid, uids[0], uids[1])
err = utils.GetData(key, &roomFriedR, fun)
// err
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
global.Logger.Errorf("查询数据失败: %v", err)
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
return err
}
// 查到了
if err == nil {
roomFriedR.RoomID = room.ID
roomFriedR.DeleteStatus = pkgEnum.NORMAL
if _, err := roomFriendQ.Select(roomFriend.RoomID, roomFriend.DeleteStatus).Where(roomFriend.ID.Eq(roomFriedR.ID)).Updates(roomFriedR); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
global.Logger.Errorf("更新房间失败 %s", err.Error())
return err
}
roomFriendR := model.RoomFriend{
Uid1: uids[0],
Uid2: uids[1],
}
defer redisCache.RemoveRoomFriend(roomFriendR)
} else {
// 创建私聊表
newRoomFriend := model.RoomFriend{
RoomID: room.ID,
Uid1: uids[0],
Uid2: uids[1],
RoomKey: strconv.FormatInt(uids[0], 10) + "," + strconv.FormatInt(uids[1], 10),
}
if err := roomFriendQ.Create(&newRoomFriend); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
global.Logger.Errorf("创建房间失败 %s", err.Error())
return err
}
}
// 自动发送一条消息
newMsg := model.Message{
RoomID: room.ID,
FromUID: userFriend.UID,
Content: "你们已经是好友了,开始聊天吧",
DeleteStatus: pkgEnum.NORMAL,
Type: enum.TextMessage,
Extra: "{}",
}
if err := service.SendTextMsg(&newMsg); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
global.Logger.Errorf("发送消息失败 %s", err.Error())
return err
}
//创建会话表
s, _ := time.ParseDuration("-1s")
if err := contactQ.Create(&model.Contact{
UID: userFriend.UID,
RoomID: room.ID,
LastMsgID: newMsg.ID,
ReadTime: time.Now(),
ActiveTime: time.Now(),
}); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
global.Logger.Errorf("创建会话失败 %s", err.Error())
return err
}
if err := contactQ.Create(&model.Contact{
UID: userFriend.FriendUID,
RoomID: room.ID,
LastMsgID: newMsg.ID,
// 读到时间设为1秒前
ReadTime: time.Now().Add(s),
ActiveTime: time.Now(),
}); err != nil {
if err := tx.Rollback(); err != nil {
global.Logger.Errorf("事务回滚失败 %s", err.Error())
return err
}
global.Logger.Errorf("创建会话失败 %s", err.Error())
return err
}
// 提交
if err := tx.Commit(); err != nil {
global.Logger.Errorf("事务提交失败 %s", err.Error())
return err
}
// 发送新消息事件
newMsgByte, _ := json.Marshal(newMsg)
msg := &primitive.Message{
Topic: enum.NewMessageTopic,
Body: newMsgByte,
}
_, _ = global.RocketProducer.SendSync(ctx, msg)
return nil
}
================================================
FILE: event/listener/new_msg_event.go
================================================
package listener
import (
"DiTing-Go/dal/model"
query "DiTing-Go/dal/query"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"DiTing-Go/pkg/utils"
wsEnum "DiTing-Go/websocket/domain/enum"
resp2 "DiTing-Go/websocket/domain/vo/resp"
"DiTing-Go/websocket/service"
"context"
"encoding/json"
"fmt"
"github.com/apache/rocketmq-client-go/v2"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
"github.com/spf13/viper"
"time"
)
func init() {
host := viper.GetString("rocketmq.host")
// 设置推送消费者
rocketSendMsgConsumer, _ := rocketmq.NewPushConsumer(
//消费组
consumer.WithGroupName(enum.NewMessageTopic+"-send-message"),
// namesrv地址
consumer.WithNameServer([]string{host}),
)
err := rocketSendMsgConsumer.Subscribe(enum.NewMessageTopic, consumer.MessageSelector{}, UpdateContactEvent)
if err != nil {
global.Logger.Panicf("subscribe error: %s", err.Error())
}
err = rocketSendMsgConsumer.Start()
if err != nil {
global.Logger.Panicf("start consumer error: %s", err.Error())
}
//// 设置推送消费者
//rocketUpdateContactConsumer, _ := rocketmq.NewPushConsumer(
// //消费组
// consumer.WithGroupName(enum.NewMessageTopic+"-update-contact"),
// // namesrv地址
// consumer.WithNameServer([]string{host}),
//)
//err := rocketUpdateContactConsumer.Subscribe(enum.NewMessageTopic, consumer.MessageSelector{}, SendMsgEvent)
//if err != nil {
// global.Logger.Panicf("subscribe error: %s", err.Error())
//}
//err = rocketUpdateContactConsumer.Start()
//if err != nil {
// global.Logger.Panicf("start consumer error: %s", err.Error())
//}
}
func UpdateContactEvent(ctx context.Context, ext ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for i := range ext {
// 解码
msg := model.Message{}
msgByte := ext[i].Message.Body
err := json.Unmarshal(msgByte, &msg)
if err != nil {
global.Logger.Errorf("jsonUtils unmarshal error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
err = updateContact(msg)
if err != nil {
global.Logger.Errorf("更新会话失败 %s", err)
return consumer.ConsumeRetryLater, nil
}
err = sendMsg(msg)
if err != nil {
global.Logger.Errorf("发送消息失败 %s", err)
return consumer.ConsumeRetryLater, nil
}
}
return consumer.ConsumeSuccess, nil
}
func updateContact(msg model.Message) error {
// 更新会话表
ctx := context.Background()
room := global.Query.Room
roomQ := room.WithContext(ctx)
roomR, err := roomQ.Where(room.ID.Eq(msg.RoomID)).First()
if err != nil {
global.Logger.Errorf("查询房间失败 %s", err)
return err
}
var uids []int64
if roomR.Type == enum.PERSONAL {
roomFriend := global.Query.RoomFriend
roomFriendQ := roomFriend.WithContext(ctx)
roomFriendR, err := roomFriendQ.Where(roomFriend.RoomID.Eq(roomR.ID)).First()
if err != nil {
global.Logger.Errorf("查询好友房间失败 %s", err)
return err
}
uids = []int64{roomFriendR.Uid1, roomFriendR.Uid2}
} else if roomR.Type == enum.GROUP {
// 查询所有群成员
roomGroup := global.Query.RoomGroup
roomGroupQ := roomGroup.WithContext(ctx)
roomGroupR, err := roomGroupQ.Where(roomGroup.RoomID.Eq(roomR.ID)).First()
if err != nil {
global.Logger.Errorf("查询群聊失败 %s", err)
return err
}
groupMember := global.Query.GroupMember
groupMemberQ := groupMember.WithContext(ctx)
groupMembers, _ := groupMemberQ.Where(groupMember.GroupID.Eq(roomGroupR.ID)).Find()
for _, groupMember := range groupMembers {
uids = append(uids, groupMember.UID)
}
}
//更新会话表
update := model.Contact{
LastMsgID: msg.ID,
UpdateTime: time.Now(),
ActiveTime: time.Now(),
}
contact := global.Query.Contact
contactQ := contact.WithContext(ctx)
_, err = contactQ.Where(contact.UID.In(uids...), contact.RoomID.Eq(msg.RoomID)).Updates(&update)
if err != nil {
global.Logger.Errorf("更新会话失败 %s", err)
return err
}
return nil
}
// SendMsgEvent 新消息事件
func SendMsgEvent(ctx context.Context, ext ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for i := range ext {
// 解码
msg := model.Message{}
msgByte := ext[i].Message.Body
err := json.Unmarshal(msgByte, &msg)
if err != nil {
global.Logger.Errorf("jsonUtils unmarshal error: %s", err.Error())
return consumer.ConsumeRetryLater, nil
}
err = sendMsg(msg)
if err != nil {
global.Logger.Errorf("发送消息失败 %s", err)
return consumer.ConsumeRetryLater, nil
}
}
return consumer.ConsumeSuccess, nil
}
// sendMsg 发送消息
func sendMsg(msg model.Message) error {
// 向房间中的所有用户发送消息,包括自己
roomQ := global.Query.WithContext(context.Background()).Room
fun := func() (interface{}, error) {
return roomQ.Where(query.Room.ID.Eq(msg.RoomID)).First()
}
room := model.Room{}
key := fmt.Sprintf(enum.RoomCacheByID, msg.RoomID)
err := utils.GetData(key, &room, fun)
if err != nil {
global.Logger.Errorf("查询房间失败 %s", err)
return err
}
msgBody := resp2.NewMessageResp{
Type: wsEnum.NewMessage,
}
str, _ := json.Marshal(msgBody)
// 单聊
if room.Type == enum.PERSONAL {
roomFriendQ := global.Query.WithContext(context.Background()).RoomFriend
roomFriendR := model.RoomFriend{}
fun = func() (interface{}, error) {
return roomFriendQ.Where(query.RoomFriend.RoomID.Eq(room.ID)).First()
}
key := fmt.Sprintf(enum.RoomFriendCacheByRoomID, room.ID)
err = utils.GetData(key, &roomFriendR, fun)
if err != nil {
global.Logger.Errorf("查询好友房间失败 %s", err)
return err
}
// 发送新消息事件
err := service.Send(roomFriendR.Uid1, str)
if err != nil {
return err
}
err = service.Send(roomFriendR.Uid2, str)
if err != nil {
return err
}
// TODO:群聊
} else if room.Type == enum.GROUP {
roomGroupQ := global.Query.WithContext(context.Background()).RoomGroup
roomGroup, _ := roomGroupQ.Where(query.RoomGroup.RoomID.Eq(room.ID)).First()
// 查询所有群成员
groupMemberQ := global.Query.WithContext(context.Background()).GroupMember
groupMembers, _ := groupMemberQ.Where(query.GroupMember.GroupID.Eq(roomGroup.ID)).Find()
// 发送新消息事件
for _, groupMember := range groupMembers {
service.Send(groupMember.UID, str)
}
}
return nil
}
================================================
FILE: event/listener/user_login_event.go
================================================
package listener
import (
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"context"
"fmt"
"github.com/apache/rocketmq-client-go/v2"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
"github.com/spf13/viper"
)
func init() {
host := viper.GetString("rocketmq.host")
// 设置推送消费者
rocketConsumer, _ := rocketmq.NewPushConsumer(
//消费组
consumer.WithGroupName(enum.UserLoginTopic+"-test"),
// namesrv地址
consumer.WithNameServer([]string{host}),
)
go test(rocketConsumer)
}
func test(rocketConsumer rocketmq.PushConsumer) {
// 必须先在 开始前
err := rocketConsumer.Subscribe(enum.UserLoginTopic, consumer.MessageSelector{}, func(ctx context.Context, ext ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for i := range ext {
fmt.Printf("subscribe callback:%v \n", ext[i])
}
return consumer.ConsumeSuccess, nil
})
if err != nil {
global.Logger.Panicf("subscribe error: %s", err.Error())
}
err = rocketConsumer.Start()
if err != nil {
global.Logger.Panicf("start consumer error: %s", err.Error())
}
}
================================================
FILE: global/init_db.go
================================================
package global
import (
"DiTing-Go/dal"
"DiTing-Go/dal/query"
"fmt"
"github.com/spf13/viper"
)
var MySQLDSN string
var Query *query.Query
func DBInit() {
MySQLDSN = fmt.Sprintf("%s:%s@tcp(%s:%s)/DiTing?charset=utf8mb4&parseTime=True&loc=Local", viper.GetString("mysql.username"), viper.GetString("mysql.password"), viper.GetString("mysql.host"), viper.GetString("mysql.port"))
dal.DB = dal.ConnectDB(MySQLDSN).Debug()
//dal.DB = dal.ConnectDB(MySQLDSN)
// 设置默认DB对象
query.SetDefault(dal.DB)
Query = query.Use(dal.DB)
}
================================================
FILE: global/init_distribute_lock.go
================================================
package global
//
//import (
// goredislib "github.com/go-redis/redis/v8"
// "github.com/go-redsync/redsync/v4"
// "github.com/go-redsync/redsync/v4/redis/goredis/v8"
// "github.com/spf13/viper"
//)
//
//var RedSync *redsync.Redsync
//
//func init() {
// Addr := viper.GetString("redis.host")
// Password := viper.GetString("redis.password")
// client := goredislib.NewClient(&goredislib.Options{
// Addr: Addr,
// Password: Password, // 密码
// DB: 0, // 数据库
// PoolSize: 20, // 连接池大小
// })
// pool := goredis.NewPool(client)
// RedSync = redsync.New(pool)
//}
================================================
FILE: global/init_evenbus.go
================================================
package global
//
//import "github.com/asaskevich/EventBus"
//
//var Bus EventBus.Bus
//
//func init() {
// Bus = EventBus.New()
//}
================================================
FILE: global/init_log.go
================================================
package global
import (
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"io"
"os"
"path"
)
var Logger *logrus.Logger
func LogInit() {
logFilePath := viper.GetString("log.log_file_path")
logFileName := viper.GetString("log.log_file_name")
//日志文件
fileName := path.Join(logFilePath, logFileName)
//写入文件
src, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
// 失败不启动
panic("日志文件写入失败, err: " + err.Error())
}
//实例化
Logger = logrus.New()
writers := []io.Writer{
src,
os.Stdout}
fileAndStdoutWriter := io.MultiWriter(writers...)
if err == nil {
Logger.SetOutput(fileAndStdoutWriter)
} else {
Logger.Info("failed to log to file.")
}
//设置日志级别
Logger.SetLevel(logrus.DebugLevel)
Logger.SetReportCaller(true)
//设置日志格式
Logger.SetFormatter(&logrus.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
})
}
================================================
FILE: global/init_minio.go
================================================
package global
import (
"github.com/minio/minio-go/v7"
)
var MinioClient *minio.Client
//func init() {
// accessKey := viper.GetString("minio.accessKey")
// accessSecret := viper.GetString("minio.accessSecret")
// endPoint := viper.GetString("minio.endPoint")
// useSSL := viper.GetBool("minio.useSSL")
// // 初始化minio客户端
// minioClient, err := minio.New(endPoint, &minio.Options{
// Creds: credentials.NewStaticV4(accessKey, accessSecret, ""),
// Secure: useSSL,
// })
// MinioClient = minioClient
// if err != nil {
// Logger.Fatal("minio client create fail, err %+v", err)
// }
//}
================================================
FILE: global/init_redis.go
================================================
package global
import (
"github.com/go-redis/redis"
"github.com/spf13/viper"
)
var Rdb *redis.Client
func RedisInit() {
Addr := viper.GetString("redis.host")
Password := viper.GetString("redis.password")
Rdb = redis.NewClient(&redis.Options{
Addr: Addr,
Password: Password, // 密码
DB: 0, // 数据库
PoolSize: 100, // 连接池大小
})
}
================================================
FILE: global/init_rocketmq.go
================================================
package global
//
//import (
// "github.com/apache/rocketmq-client-go/v2"
// "github.com/apache/rocketmq-client-go/v2/producer"
// "github.com/spf13/viper"
//)
//
//var RocketProducer rocketmq.Producer
//
//func init() {
// host := viper.GetString("rocketmq.host")
// group := viper.GetString("rocketmq.group")
// RocketProducer, _ = rocketmq.NewProducer(
// // 设置 nameSrvAddr
// // nameSrvAddr 是 Topic 路由注册中心
// producer.WithNameServer([]string{host}),
// // 指定发送失败时的重试时间
// producer.WithRetry(3),
// // 设置 Group
// producer.WithGroupName(group),
// )
// // 开始连接
// err := RocketProducer.Start()
// if err != nil {
// Logger.Panicf("start producer error: %s", err.Error())
// }
//}
================================================
FILE: global/init_time.go
================================================
package global
//import "time"
//
//func init() {
// location, _ := time.LoadLocation("Asia/Shanghai")
// time.Local = location
//}
================================================
FILE: go.mod
================================================
module DiTing-Go
go 1.22.1
require (
github.com/apache/rocketmq-client-go/v2 v2.1.2
github.com/gin-gonic/gin v1.9.1
github.com/go-redis/redis v6.15.9+incompatible
github.com/goccy/go-json v0.10.2
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/gorilla/websocket v1.5.1
github.com/jinzhu/copier v0.4.0
github.com/minio/minio-go/v7 v7.0.69
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
gorm.io/driver/mysql v1.5.6
gorm.io/gen v0.3.25
gorm.io/gorm v1.25.9
gorm.io/plugin/dbresolver v1.5.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/bytedance/sonic v1.11.3 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/golang/mock v1.3.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tidwall/gjson v1.13.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/datatypes v1.2.0 // indirect
gorm.io/driver/sqlite v1.5.5 // indirect
gorm.io/hints v1.1.2 // indirect
stathat.com/c/consistent v1.0.0 // indirect
)
================================================
FILE: go.sum
================================================
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/apache/rocketmq-client-go/v2 v2.1.2 h1:yt73olKe5N6894Dbm+ojRf/JPiP0cxfDNNffKwhpJVg=
github.com/apache/rocketmq-client-go/v2 v2.1.2/go.mod h1:6I6vgxHR3hzrvn+6n/4mrhS+UTulzK/X9LB2Vk1U5gE=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA=
github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA=
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gen v0.3.25 h1:uT/1YfvcnYUdike4XPYyi89FEnVHZF115GUXQm2Sfug=
gorm.io/gen v0.3.25/go.mod h1:p+t0iCKjaPz+pKRxcx63nXdRgnrah/QD2l92747ihyA=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
gorm.io/plugin/dbresolver v1.5.1 h1:s9Dj9f7r+1rE3nx/Ywzc85nXptUEaeOO0pt27xdopM8=
gorm.io/plugin/dbresolver v1.5.1/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c=
stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0=
================================================
FILE: logic/captcha.go
================================================
package logic
import (
"DiTing-Go/global"
"DiTing-Go/utils"
"encoding/json"
"fmt"
"math/rand"
"time"
)
// CheckCaptcha 检查验证码是否正确
func CheckCaptcha(captchaId, captchaValue string) bool {
if captchaId == "" || captchaValue == "" {
return false
}
// 根据captchaId从redis查
captchaByte, err := utils.GetValueFromRedis(captchaId)
if err != nil {
global.Logger.Errorf("get captcha from redis error: %v", err)
return false
}
var captcha string
err = json.Unmarshal(captchaByte, &captcha)
if err != nil {
global.Logger.Errorf("json unmarshal error: %v", err)
return false
}
if captchaValue == "1234" {
global.Logger.Infof("captcha is 1234, pass")
return true
}
// 1234直接放行
return captchaValue == captcha
}
// GenerateCaptcha 生成验证码
func GenerateCaptcha(captchaId string) (string, error) {
// 生成四位随机数
rand.Seed(time.Now().UnixNano())
captchaVal := rand.Intn(10000) // 生成1000~9999之间的随机数
val := fmt.Sprintf("%04d", captchaVal) // 格式化为四位数,不足前面补0
// 将验证码存入redis
captchaKey := utils.MakeUserCaptchaKey(captchaId)
err := utils.SetValueToRedis(captchaKey, val, 1*time.Minute)
if err != nil {
return "", err
}
return val, nil
}
// SendCaptcha 发送验证码
func SendCaptcha(phone, captcha string) error {
return nil
}
================================================
FILE: logic/login.go
================================================
package logic
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"DiTing-Go/utils"
"context"
"strconv"
"github.com/go-redis/redis"
"github.com/goccy/go-json"
"github.com/pkg/errors"
)
// CheckPassword 校验用户名密码是否匹配
func CheckPassword(ctx context.Context, phone, password string) bool {
// 参数验证
if phone == "" || password == "" {
global.Logger.Warnf("CheckPassword: phone or password is empty, phone: %s", phone)
return false
}
// 密码长度必须大于6
if len(password) < 6 {
global.Logger.Warnf("CheckPassword: password too short, phone: %s, password length: %d", phone, len(password))
return false
}
// 对密码进行md5加密
encryptedPassword := utils.EncryptPassword(password)
// 首先尝试从Redis获取用户ID
userPhoneKey := utils.MakeUserPhoneKey(phone)
userIdBytes, err := utils.GetValueFromRedis(userPhoneKey)
if err == nil && len(userIdBytes) > 0 {
userId := string(userIdBytes)
global.Logger.Infof("CheckPassword: get userId from redis success, phone: %s, userId: %s", phone, userId)
// 从Redis获取用户信息
userInfo, err := GetUserInfo2Redis(userId)
if err != nil {
if errors.Is(err, redis.Nil) {
global.Logger.Infof("CheckPassword: user info not found in redis, phone: %s, userId: %s", phone, userId)
} else {
global.Logger.Errorf("CheckPassword: get user info from redis failed, phone: %s, userId: %s, err: %v", phone, userId, err)
}
// Redis获取失败,继续查询数据库
} else {
// Redis获取成功,比较密码
global.Logger.Infof("CheckPassword: get user info from redis success, phone: %s, userId: %s", phone, userId)
return userInfo.Password == encryptedPassword
}
} else if errors.Is(err, redis.Nil) {
global.Logger.Infof("CheckPassword: userId not found in redis, phone: %s", phone)
} else {
global.Logger.Errorf("CheckPassword: get userId from redis failed, phone: %s, err: %v", phone, err)
}
// Redis查询失败,从数据库查询
global.Logger.Infof("CheckPassword: querying user from database, phone: %s", phone)
userInfo, err := utils.QueryUserByPhone(ctx, phone)
if err != nil {
global.Logger.Errorf("CheckPassword: failed to query user by phone, phone: %s, err: %v", phone, err)
return false
}
if userInfo == nil {
global.Logger.Warnf("CheckPassword: user not found in database, phone: %s", phone)
return false
}
// 比较密码
passwordMatch := userInfo.Password == encryptedPassword
global.Logger.Infof("CheckPassword: password check result, phone: %s, match: %v", phone, passwordMatch)
return passwordMatch
}
func SetUserInfo2Redis(userInfo model.User) error {
userInfoByte, err := json.Marshal(userInfo)
if err != nil {
global.Logger.Errorf("userInfo %v,failed to marshal user info: %v", userInfo, err)
return err
}
if err := utils.SetValueToRedis(strconv.FormatInt(userInfo.ID, 10), string(userInfoByte), enum.DefaultCacheTime); err != nil {
global.Logger.Errorf("userInfo %v,failed to set user info to redis: %v", userInfo, err)
return err
}
return nil
}
func GetUserInfo2Redis(userId string) (model.User, error) {
if userId == "" || userId == "0" {
return model.User{}, errors.New("userId is zero")
}
userInfoByte, err := utils.GetValueFromRedis(userId)
if err != nil {
if errors.Is(err, redis.Nil) {
global.Logger.Infof("userId %s, user info not found in redis", userId)
return model.User{}, err
}
global.Logger.Errorf("userId %s, failed to get user info from redis: %v", userId, err)
return model.User{}, err
}
userInfo := model.User{}
if err := json.Unmarshal([]byte(userInfoByte), &userInfo); err != nil {
global.Logger.Errorf("userId %s, failed to unmarshal user info: %v", userId, err)
return model.User{}, err
}
global.Logger.Infof("userId %s, user info found in redis: %v", userId, userInfo)
return userInfo, nil
}
func GetUserInfo2DB(phone string) (model.User, error) {
if phone == "" {
return model.User{}, errors.New("phone is empty")
}
ctx := context.Background()
userInfo, err := utils.QueryUserByPhone(ctx, phone)
if err != nil {
global.Logger.Errorf("phone %s, failed to query user by phone: %v", phone, err)
return model.User{}, err
}
global.Logger.Infof("phone %s, user info found in db: %v", phone, userInfo)
return *userInfo, nil
}
func GetUserInfo2DBById(ctx context.Context, userId string) (model.User, error) {
if userId == "" {
return model.User{}, errors.New("userId is zero")
}
userInfo, err := utils.QueryUserByID(ctx, userId)
if err != nil {
global.Logger.Errorf("userId %s, failed to query user by phone: %v", userId, err)
return model.User{}, err
}
global.Logger.Infof("userId %s, user info found in db: %v", userId, userInfo)
return *userInfo, nil
}
================================================
FILE: logic/register.go
================================================
package logic
import (
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"DiTing-Go/utils"
"context"
"errors"
"fmt"
"github.com/go-redis/redis"
"gorm.io/gorm"
)
// CheckCaptchaProcess 检查验证码
func CheckCaptchaProcess(phone, captcha string) bool {
// 检查验证码
phoneKey := utils.MakeUserCaptchaKey(phone)
if !CheckCaptcha(phoneKey, captcha) {
global.Logger.Infof("验证码错误")
return false
}
return true
}
// CheckCaptchaExist 检查验证码是否存在
func CheckCaptchaExist(phone string) bool {
// 检查验证码
phoneKey := utils.MakeUserCaptchaKey(phone)
_, err := utils.GetValueFromRedis(phoneKey)
if errors.Is(err, redis.Nil) {
global.Logger.Infof("phoneKey:%s, 验证码不存在", phoneKey)
return false
} else if err != nil {
global.Logger.Errorf("phoneKey:%s, 查询验证码失败: %v", phoneKey, err)
return false
}
global.Logger.Infof("phoneKey:%s, 验证码存在", phoneKey)
return true
}
// CheckPhoneInRedis 检查手机号在redis中是否存在
func CheckPhoneInRedis(phone string) bool {
phoneKey := utils.MakeUserPhoneKey(phone)
rst, err := utils.GetValueFromRedis(phoneKey)
// 如果redis没查到,查数据库
if errors.Is(err, redis.Nil) {
return false
}
// 如果redis查询出错,查数据库
if err != nil {
global.Logger.Errorf("phoneKey:%s, 查询手机号失败: %v", phoneKey, err)
return false
}
// 如果redis查到了,直接返回
if rst != nil {
return true
}
return false
}
// CheckPhoneInDB 检查手机号在db中是否存在
func CheckPhoneInDB(ctx context.Context, phone string) (bool, error) {
// 如果redis查不到,查数据库
user, err := utils.QueryUserByPhone(ctx, phone)
// 数据库查询出错,返回失败
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
global.Logger.Errorf("phone:%s, 查询手机号失败: %v", phone, err)
return true, err
}
//数据库查到了,返回失败,且用户状态为正常
if user != nil && user.Status == enum.UserStatusNormal {
// 将用户信息存入redis
phoneKey := utils.MakeUserPhoneKey(phone)
// 将phone->user.ID存入redis
if err := utils.SetValueToRedis(phoneKey, fmt.Sprintf("%d", user.ID), enum.DefaultCacheTime); err != nil {
global.Logger.Errorf("phone:%s, 设置phoneUid映射 redis失败: %v", phone, err)
return true, err
}
global.Logger.Infof("phone:%s, 用户已存在", phone)
return true, err
}
return false, nil
}
================================================
FILE: logic/user.go
================================================
package logic
import (
"DiTing-Go/dal/model"
"DiTing-Go/dal/query"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"context"
"github.com/pkg/errors"
"gorm.io/gorm"
)
func CreateUser(ctx context.Context, userInfo model.User) error {
user := query.User
userQ := user.WithContext(ctx)
userId := userInfo.ID
// 检查用户是否存在
userResult, err := userQ.Where(user.ID.Eq(userId)).First()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
global.Logger.Errorf("user:%v, 检查用户是否存在失败: %v", userResult, err)
return err
}
// 将用户状态更新为正常
if userResult != nil && userResult.Status == int32(enum.UserStatusCancel) {
global.Logger.Errorf("user:%v, 用户已注销", userResult)
userInfo.Status = enum.UserStatusNormal
if err := userQ.Save(&userInfo); err != nil {
global.Logger.Errorf("user:%v, 更新用户状态失败: %v", userResult, err)
return err
}
} else {
// 创建对象
if err := userQ.Create(&userInfo); err != nil {
global.Logger.Errorf("user:%v, 创建用户失败: %v", userResult, err)
return err
}
}
return nil
}
================================================
FILE: logic/user_cancle.go
================================================
package logic
import (
"DiTing-Go/dal/query"
"DiTing-Go/domain/enum"
"DiTing-Go/global"
"DiTing-Go/utils"
"context"
"strconv"
)
// DeleteUserInfoFromRedis 删除用户缓存
func DeleteUserInfoFromRedis(userId string) error {
if err := utils.DeleteValueFromRedis(userId); err != nil {
global.Logger.Errorf("删除用户缓存失败: userId=%s, err=%v", userId, err)
return err
}
return nil
}
// DeleteUserInfoFromDB 删除用户数据库,软删除
func DeleteUserInfoFromDB(ctx context.Context, userId string) error {
user := query.User
userQ := user.WithContext(ctx)
userIdInt, err := strconv.ParseInt(userId, 10, 64)
if err != nil {
global.Logger.Errorf("转换用户ID失败: userId=%s, err=%v", userId, err)
return err
}
if _, err := userQ.Where(user.ID.Eq(userIdInt)).Update(user.Status, enum.UserStatusCancel); err != nil {
global.Logger.Errorf("删除用户数据库失败: userId=%s, err=%v", userId, err)
return err
}
return nil
}
================================================
FILE: main.go
================================================
package main
import (
//_ "DiTing-Go/event/listener"
"DiTing-Go/global"
"DiTing-Go/routes"
"DiTing-Go/utils/setting"
)
// swagger 中添加header.Authorization:token 校验 token
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
func main() {
// 初始化配置
setting.ConfigInit()
// 初始化数据库连接
global.DBInit()
//初始化redis连接
global.RedisInit()
global.LogInit()
routes.InitRouter()
}
================================================
FILE: pkg/domain/enum/code.go
================================================
package enum
const (
SUCCESS = 200
ERROR = 500
INVALID_PARAMS = 400
ERROR_EXIST_TAG = 10001
ERROR_NOT_EXIST_TAG = 10002
ERROR_NOT_EXIST_ARTICLE = 10003
ERROR_AUTH_CHECK_TOKEN_FAIL = 20001
ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
ERROR_AUTH_TOKEN = 20003
ERROR_AUTH = 20004
)
================================================
FILE: pkg/domain/enum/common_enum.go
================================================
package enum
const (
YES = 2
NO = 1
NORMAL = 1
DELETED = 2
)
================================================
FILE: pkg/domain/enum/msg.go
================================================
package enum
var MsgFlags = map[int]string{
SUCCESS: "ok",
ERROR: "fail",
INVALID_PARAMS: "请求参数错误",
ERROR_AUTH_CHECK_TOKEN_FAIL: "Token鉴权失败",
ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token已超时",
ERROR_AUTH_TOKEN: "Token生成失败",
ERROR_AUTH: "Token错误",
}
func GetMsg(code int) string {
msg, ok := MsgFlags[code]
if ok {
return msg
}
return MsgFlags[ERROR]
}
================================================
FILE: pkg/domain/vo/req/page_req.go
================================================
package req
type PageReq struct {
Cursor *string `json:"cursor" form:"cursor"`
PageSize int `json:"pageSize" form:"pageSize" binding:"required"`
}
================================================
FILE: pkg/domain/vo/resp/page_resp.go
================================================
package resp
type PageResp struct {
Cursor *string `json:"cursor" form:"cursor"`
IsLast bool `json:"isLast" form:"is_last"`
Data any `json:"data" form:"data"`
}
================================================
FILE: pkg/domain/vo/resp/response.go
================================================
package resp
import (
"DiTing-Go/pkg/domain/enum"
"github.com/gin-gonic/gin"
)
// ResponseData 表示统一响应的JSON格式
type ResponseData struct {
Success bool `json:"success"` // 是否成功
Code int `json:"code"` // 状态码
Message string `json:"message"` // 响应消息
Data interface{} `json:"data"` // 响应数据
}
// ErrorResponse 是一个辅助函数,用于创建错误响应
// 参数:
//
// c *gin.Context:Gin上下文对象,用于处理HTTP请求和响应
// code int:500
// message string:响应消息,用于描述响应的错误信息或提示信息
func ErrorResponse(c *gin.Context, message string) {
c.JSON(enum.ERROR, ResponseData{
Code: enum.ERROR,
Success: false,
Message: message,
Data: nil,
})
}
// SuccessResponse 是一个辅助函数,用于创建成功响应
// 参数:
//
// c *gin.Context:Gin上下文对象,用于处理HTTP请求和响应
// code int:200
// data interface{}:响应数据,用于描述请求处理成功后返回的具体数据
func SuccessResponse(c *gin.Context, data interface{}) {
c.JSON(enum.SUCCESS, ResponseData{
Code: enum.SUCCESS,
Success: true,
Message: "success",
Data: data,
})
}
func SuccessResponseWithMsg(c *gin.Context, msg string) {
c.JSON(enum.SUCCESS, ResponseData{
Code: enum.SUCCESS,
Success: true,
Message: msg,
Data: nil,
})
}
func ReturnSuccessResponse(c *gin.Context, response ResponseData) {
c.JSON(enum.SUCCESS, response)
}
func ReturnErrorResponse(c *gin.Context, response ResponseData) {
c.JSON(enum.ERROR, response)
}
// ErrorResponseData 是一个辅助函数,用于创建错误响应
func ErrorResponseData(msg string) ResponseData {
return ResponseData{
Code: enum.ERROR,
Success: false,
Message: msg,
Data: nil,
}
}
// SuccessResponseData 是一个辅助函数,用于创建成功响应
func SuccessResponseData(data interface{}) ResponseData {
return ResponseData{
Code: enum.SUCCESS,
Success: true,
Message: "success",
Data: data,
}
}
// SuccessResponseDataWithMsg 是一个辅助函数,用于创建成功响应
func SuccessResponseDataWithMsg(msg string) ResponseData {
return ResponseData{
Code: enum.SUCCESS,
Success: true,
Message: msg,
Data: nil,
}
}
================================================
FILE: pkg/utils/cursor_utils.go
================================================
package utils
import (
pkgReq "DiTing-Go/pkg/domain/vo/req"
pkgResp "DiTing-Go/pkg/domain/vo/resp"
"fmt"
"github.com/pkg/errors"
"gorm.io/gorm"
"reflect"
"regexp"
"time"
)
// Paginate 是通用的游标分页函数
// TODO: select部分字段
func Paginate(db *gorm.DB, params pkgReq.PageReq, result interface{}, cursorFieldName string, isAsc bool, conditions ...interface{}) (*pkgResp.PageResp, error) {
var resp pkgResp.PageResp
query := db
if len(conditions) > 0 {
query = query.Where(conditions[0], conditions[1:]...)
}
if params.Cursor != nil && *params.Cursor != "" {
query = query.Where(fmt.Sprintf("%s < ?", cursorFieldName), *params.Cursor)
}
if isAsc {
query = query.Order(fmt.Sprintf("%s ASC", cursorFieldName))
} else {
query = query.Order(fmt.Sprintf("%s DESC", cursorFieldName))
}
query = query.Limit(params.PageSize).Find(result)
if query.Error != nil {
return &resp, query.Error
}
// 获取查询结果的切片值
slice := reflect.ValueOf(result).Elem()
// 根据记录条数是否等于页大小判断是否是最后一页
lastItemIndex := slice.Len()
if lastItemIndex < params.PageSize {
resp.IsLast = true
} else {
resp.IsLast = false
}
// 通过反射获取cursorFieldName对应的值
if lastItemIndex > 0 {
lastItem := slice.Index(lastItemIndex - 1)
fieldsMap, err := GetTagName(result)
if err != nil {
return nil, err
}
cursorValue := lastItem.FieldByName(fieldsMap[cursorFieldName])
//获取cursorValue的类型
cursorType := cursorValue.Type()
// 如果是Time.time类型,转换为时间戳
cursorStr := ""
if cursorType.String() == "time.Time" {
cursorStr = fmt.Sprint(cursorValue.Interface().(time.Time).UnixNano())
} else {
cursorStr = fmt.Sprint(cursorValue.Interface())
}
resp.Cursor = &cursorStr
}
resp.Data = result
return &resp, nil
}
// GetTagName 获取结构体中Tag的值,如果没有tag则返回字段值
func GetTagName(structName interface{}) (map[string]string, error) {
t := reflect.TypeOf(structName).Elem().Elem()
fieldNum := t.NumField()
result := make(map[string]string, fieldNum)
for i := 0; i < fieldNum; i++ {
fieldName := t.Field(i).Name
tag := t.Field(i).Tag.Get("gorm")
if tag == "" {
result[fieldName] = fieldName
} else {
// 定义正则表达式
re := regexp.MustCompile(`column:([^;]+)`)
// 使用正则表达式查找匹配项
match := re.FindStringSubmatch(tag)
column := fieldName
if len(match) > 1 {
column = match[1]
} else {
return nil, errors.New("without column error")
}
result[column] = fieldName
}
}
return result, nil
}
================================================
FILE: pkg/utils/redis.go
================================================
package utils
import (
domainEnum "DiTing-Go/domain/enum"
"DiTing-Go/global"
"github.com/go-redis/redis"
"github.com/goccy/go-json"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
// SetString 设置字符串
func SetString(key string, value any) error {
valueByte, err := json.Marshal(value)
if err = global.Rdb.Set(key, valueByte, domainEnum.DefaultCacheTime).Err(); err != nil {
return errors.New("redis set error")
}
return nil
}
// GetString 获取字符串
func GetString(key string, value any) error {
valueByte, err := global.Rdb.Get(key).Result()
if err != nil && errors.Is(err, redis.Nil) {
return err
} else if err != nil {
return errors.New("redis get error")
}
err = json.Unmarshal([]byte(valueByte), value)
if err != nil {
return errors.New("jsonUtils unmarshal error")
}
return nil
}
// GetData 获取数据
func GetData(cacheKey string, value any, dbQueryFunc func() (interface{}, error)) error {
// 1. 从缓存中获取数据
err := GetString(cacheKey, value)
// 查询到数据
if err == nil {
return nil
} else if !errors.Is(err, redis.Nil) {
return err
}
err = QueryAndSet(cacheKey, value, dbQueryFunc)
if err != nil {
return err
}
return nil
}
// QueryAndSet 查询数据库并设置缓存
func QueryAndSet(cacheKey string, value any, dbQueryFunc func() (interface{}, error)) error {
// 2. 从数据库中获取数据
result, err := dbQueryFunc()
if err != nil {
return err
}
err = copier.Copy(value, result)
if err != nil {
global.Logger.Errorf("拷贝数据失败: %v", err)
return err
}
// 3. 将查询结果写回缓存
if err = SetString(cacheKey, result); err != nil {
global.Logger.Errorf("写入redis失败: %v", err)
return err
}
return err
}
func RemoveData(key string) {
global.Rdb.Del(key)
}
================================================
FILE: pkg/utils/redis_lock.go
================================================
package utils
//type RedSyncLock struct {
// mutex *redsync.Mutex
// ctx context.Context
// cancel context.CancelFunc
//}
//
//// GetLock 获取分布式锁
//func GetLock(key string) (*RedSyncLock, error) {
// ctx, cancel := context.WithCancel(context.Background())
// mutex := global.RedSync.NewMutex(key)
// if err := mutex.LockContext(ctx); err != nil {
// cancel()
// global.Logger.Errorf("加锁失败 %s", err)
// return nil, errors.New("Business Error")
// }
// // 开启一个goroutine,周期性地续租锁
// go func() {
// ticker := time.NewTicker(5 * time.Second) // 按照需求调整
// defer ticker.Stop()
// for {
// select {
// case <-ticker.C:
// ok, err := mutex.Extend()
// //fmt.Printf("续租锁 %s\n", ok)
// if err != nil {
// global.Logger.Errorf("Failed to extend lock: %s", err)
// return
// } else if !ok {
// global.Logger.Errorf("Failed to extend lock: %s", fmt.Errorf("lock %s is not held", key))
// return
// }
// case <-ctx.Done():
// return
// }
// }
// }()
//
// return &RedSyncLock{
// mutex: mutex,
// ctx: ctx,
// cancel: cancel,
// }, nil
//}
//
//// ReleaseLock 释放分布式锁, 释放锁失败不影响业务
//func ReleaseLock(lock *RedSyncLock) {
// lock.cancel()
// mutex := lock.mutex
// _, err := mutex.Unlock()
// if err != nil {
// global.Logger.Errorf("解锁失败 %s", err)
// }
//}
================================================
FILE: pkg/utils/sort.go
================================================
package utils
type Int64Slice []int64
func (p Int64Slice) Len() int {
return len(p)
}
func (p Int64Slice) Less(i, j int) bool {
return p[i] < p[j]
}
func (p Int64Slice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
================================================
FILE: routes/init_router.go
================================================
package routes
import (
"DiTing-Go/controller"
_ "DiTing-Go/docs"
"DiTing-Go/pkg/domain/vo/resp"
"DiTing-Go/utils/middleware"
"DiTing-Go/websocket/global"
websocketService "DiTing-Go/websocket/service"
"log"
"net/http"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// InitRouter 初始化路由
func InitRouter() {
//go initWebSocket()
initGin()
}
// 初始化websocket
func initWebSocket() {
http.HandleFunc("/websocket", websocketService.Connect)
log.Fatal(http.ListenAndServe("localhost:5001", nil))
}
// 初始化gin
func initGin() {
router := gin.Default()
router.Use(middleware.LoggerToFile())
router.Use(middleware.Cors())
//添加swagger访问路由
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// 不需要身份验证的路由
apiPublic := router.Group("/api/public")
{
// 用户注册
apiPublic.POST("/register", controller.RegisterController)
// 验证码发送
apiPublic.POST("/captcha", controller.CaptchaController)
// 用户登录
apiPublic.POST("/login", controller.LoginController)
}
apiUser := router.Group("/api/user")
apiUser.Use(middleware.JWT())
{
// 注销账户
apiUser.DELETE("/cancel", controller.CancelController)
// //添加好友
// apiUser.POST("/add", controller.ApplyFriendController)
// //删除好友
// apiUser.DELETE("/delete/", controller.DeleteFriendController)
// //同意好友申请
// apiUser.PUT("/agree", controller.AgreeFriendController)
// //获取好友申请列表
// apiUser.GET("/getApplyList", controller.GetUserApplyController)
// //获取好友列表
// apiUser.GET("/getFriendList", controller.GetFriendListController)
// // 判断是否是好友
// apiUser.GET("/isFriend/:friendUid", controller.IsFriendController)
// //好友申请未读数量
// apiUser.GET("/unreadApplyNum", controller.UnreadApplyNumController)
// //根据好友昵称搜索好友
// apiUser.GET("/getUserInfoByName", controller.GetUserInfoByNameController)
// // TODO:测试使用
// apiUser.GET("/test", test)
}
//apiGroup := router.Group("/api/group")
//apiGroup.Use(middleware.JWT())
//{
// //创建群聊
// apiGroup.POST("/create", controller.CreateGroupController)
// apiGroup.DELETE("/:id", service.DeleteGroupService)
// apiGroup.POST("/join", service.JoinGroupService)
// apiGroup.POST("/quit", service.QuitGroupService)
// apiGroup.GET("/getGroupMemberList", service.GetGroupMemberListService)
// apiGroup.POST("/grantAdministrator", service.GrantAdministratorService)
// apiGroup.POST("/removeAdministrator", service.RemoveAdministratorService)
//}
//
//apiContact := router.Group("/api/contact")
//apiContact.Use(middleware.JWT())
//{
// apiContact.GET("getContactList", controller.GetContactListController)
// apiContact.GET("getNewContactList", controller.GetNewContactListController)
// apiContact.GET("getMessageList", service.GetContactDetailService)
// apiContact.GET("getNewMsgList", controller.GetNewMsgListController)
// apiContact.POST("userInfo/batch", controller.GetUserInfoBatchController)
//}
//
//apiMsg := router.Group("/api/chat")
//apiMsg.Use(middleware.JWT())
//{
// apiMsg.POST("msg", controller.SendMessageController)
//}
//apiFile := router.Group("/api/file")
//apiFile.Use(middleware.JWT())
//{
// apiFile.GET("getPreSigned", service.GetPreSigned)
//}
err := router.Run(":5000")
if err != nil {
return
}
}
// TODO:测试使用
func test(c *gin.Context) {
msg := new(global.Msg)
msg.Uid = 2
websocketService.Send(msg.Uid, []byte("{\"type\":4}"))
resp.SuccessResponse(c, nil)
}
================================================
FILE: service/adapter/build_contact_dao_list.go
================================================
package adapter
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/dto"
"DiTing-Go/domain/enum"
cmap "github.com/orcaman/concurrent-map/v2"
"strconv"
)
type RoomDto struct {
ID int64
Avatar string
Name string
Type int
}
func BuildContactDaoList(contactList []model.Contact, userList []*model.User, messageList []*model.Message, roomList []*model.Room, roomFriendList []*model.RoomFriend, roomGroupList []*model.RoomGroup, countMap cmap.ConcurrentMap[string, int64]) []dto.ContactDto {
contactDtoList := make([]dto.ContactDto, 0)
userMap := make(map[int64]*model.User)
for _, user := range userList {
userMap[user.ID] = user
}
msgMap := make(map[int64]*model.Message)
for _, msg := range messageList {
msgMap[msg.ID] = msg
}
roomFriendMap := make(map[int64]*model.RoomFriend)
roomGroupMap := make(map[int64]*model.RoomGroup)
for _, roomFriend := range roomFriendList {
roomFriendMap[roomFriend.RoomID] = roomFriend
}
for _, roomGroup := range roomGroupList {
roomGroupMap[roomGroup.RoomID] = roomGroup
}
roomMap := make(map[int64]RoomDto)
for _, room := range roomList {
roomDto := RoomDto{}
roomDto.ID = room.ID
if room.Type == enum.PERSONAL {
userId := roomFriendMap[room.ID].Uid1
if userId == contactList[0].UID {
userId = roomFriendMap[room.ID].Uid2
}
user := userMap[userId]
roomDto.Avatar = user.Avatar
roomDto.Name = user.Name
roomDto.Type = enum.PERSONAL
} else {
roomGroup := roomGroupMap[room.ID]
roomDto.Avatar = roomGroup.Avatar
roomDto.Name = roomGroup.Name
roomDto.Type = enum.GROUP
}
roomMap[room.ID] = roomDto
}
for _, contact := range contactList {
contactDto := dto.ContactDto{}
contactDto.ID = contact.ID
contactDto.RoomID = contact.RoomID
contactDto.Avatar = roomMap[contact.RoomID].Avatar
contactDto.Name = roomMap[contact.RoomID].Name
if msgMap[contact.LastMsgID] != nil {
contactDto.LastMsg = msgMap[contact.LastMsgID].Content
}
contactDto.LastTime = contact.ActiveTime.UnixMilli()
//TODO:统计未读消息数
unreadCount, _ := countMap.Get(strconv.FormatInt(contact.RoomID, 10))
contactDto.UnreadCount = int32(unreadCount)
contactDto.Type = roomMap[contact.RoomID].Type
contactDtoList = append(contactDtoList, contactDto)
}
return contactDtoList
}
================================================
FILE: service/adapter/build_message_resp.go
================================================
package adapter
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/vo/resp"
)
func BuildMessageRespByMsgAndUser(msgList *[]model.Message, userMap map[int64]*model.User) []resp.MessageResp {
var messageRespList []resp.MessageResp
for i := range len(*msgList) {
messageResp := resp.MessageResp{}
msg := (*msgList)[i]
msgUser := resp.MsgUser{}
msgUser.Uid = userMap[msg.FromUID].ID
msgUser.Username = userMap[msg.FromUID].Name
msgUser.Avatar = userMap[msg.FromUID].Avatar
messageResp.FromUser = msgUser
message := resp.Msg{}
message.ID = msg.ID
message.RoomId = msg.RoomID
message.Type = msg.Type
message.Body.Content = msg.Content
message.Body.Reply = msg.ReplyMsgID
messageResp.Message = message
messageResp.SendTime = msg.CreateTime.UnixNano()
messageRespList = append(messageRespList, messageResp)
}
return messageRespList
}
================================================
FILE: service/adapter/build_user_info_by_name_resp.go
================================================
package adapter
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/vo/resp"
)
func BuildUserInfoByNameResp(userList []*model.User, userApply []*model.UserApply, userFriend []*model.UserFriend) []resp.GetUserInfoByNameResp {
userApplyMap := make(map[int64]*model.UserApply)
for _, apply := range userApply {
userApplyMap[apply.TargetID] = apply
}
userFriendMap := make(map[int64]*model.UserFriend)
for _, friend := range userFriend {
userFriendMap[friend.FriendUID] = friend
}
getUserInfoByNameRespList := make([]resp.GetUserInfoByNameResp, 0)
for _, user := range userList {
getUserInfoByNameResp := resp.GetUserInfoByNameResp{
Uid: user.ID,
Name: user.Name,
Avatar: user.Avatar,
}
//TODO:抽象为常量
if userFriendMap[user.ID] != nil {
getUserInfoByNameResp.Status = 3
} else if userApplyMap[user.ID] != nil {
getUserInfoByNameResp.Status = 2
} else {
getUserInfoByNameResp.Status = 1
}
getUserInfoByNameRespList = append(getUserInfoByNameRespList, getUserInfoByNameResp)
}
return getUserInfoByNameRespList
}
================================================
FILE: service/captcha_service.go
================================================
package service
import (
"DiTing-Go/domain/vo/req"
"DiTing-Go/global"
"DiTing-Go/logic"
pkgResp "DiTing-Go/pkg/domain/vo/resp"
)
// CaptchaService 验证码发送
func CaptchaService(captchaReq req.CaptchaReq) (pkgResp.ResponseData, error) {
if captchaReq.Phone == "" {
global.Logger.Infof("手机号不能为空")
return pkgResp.ErrorResponseData("手机号不能为空"), nil
}
//检查验证码是否未过期,未过期不生成
if logic.CheckCaptchaExist(captchaReq.Phone) {
global.Logger.Infof("手机号:%s,验证码未过期", captchaReq.Phone)
return pkgResp.ErrorResponseData("发送频率过高,请稍后再试"), nil
}
// 生成验证码
captcha, err := logic.GenerateCaptcha(captchaReq.Phone)
if err != nil {
global.Logger.Errorf("phone:%s ,生成验证码失败: %v", captchaReq.Phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试"), err
}
// 发送验证码
// TODO:空实现,实际应用中可以使用短信服务商的SDK发送验证码
if err := logic.SendCaptcha(captchaReq.Phone, captcha); err != nil {
global.Logger.Errorf("phone:%s ,发送验证码失败: %v", captchaReq.Phone, err)
return pkgResp.ErrorResponseData("验证码发送失败,请稍后再试"), err
}
return pkgResp.SuccessResponseDataWithMsg("验证码发送成功"), nil
}
================================================
FILE: service/contact_service.go
================================================
package service
//
//import (
// "DiTing-Go/dal"
// "DiTing-Go/dal/model"
// "DiTing-Go/domain/enum"
// "DiTing-Go/domain/vo/req"
// domainResp "DiTing-Go/domain/vo/resp"
// "DiTing-Go/global"
// pkgEnum "DiTing-Go/pkg/domain/enum"
// pkgReq "DiTing-Go/pkg/domain/vo/req"
// "DiTing-Go/pkg/domain/vo/resp"
// pkgResp "DiTing-Go/pkg/domain/vo/resp"
// "DiTing-Go/pkg/utils"
// "DiTing-Go/service/adapter"
// "context"
// "github.com/gin-gonic/gin"
// cmap "github.com/orcaman/concurrent-map/v2"
// "github.com/pkg/errors"
// "strconv"
// "sync"
// "time"
//)
//
//func GetContactListService(uid int64, pageReq pkgReq.PageReq) (pkgResp.ResponseData, error) {
// db := dal.DB
// contact := make([]model.Contact, 0)
// condition := []interface{}{"uid=?", strconv.FormatInt(uid, 10)}
// if pageReq.Cursor != nil && *pageReq.Cursor != "" {
// // 时间戳转时间
// timestamp, err := strconv.ParseInt(*pageReq.Cursor, 10, 64)
// if err != nil {
// global.Logger.Errorf("时间戳转换失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// cursor := time.Unix(0, timestamp)
// cursorStr := cursor.Format(time.RFC3339Nano)
// pageReq.Cursor = &cursorStr
// }
//
// pageResp, err := utils.Paginate(db, pageReq, &contact, "active_time", false, condition...)
// if err != nil {
// global.Logger.Errorf("查询会话列表失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// contactList := pageResp.Data.(*[]model.Contact)
//
// // 收集会话id
// contactRoomIdList := make([]int64, 0)
// for _, contact := range *contactList {
// contactRoomIdList = append(contactRoomIdList, contact.RoomID)
// }
//
// // 查询出对应的房间信息
// ctx := context.Background()
// room := global.Query.Room
// roomQ := room.WithContext(ctx)
// // 查询房间类型
// roomRList, err := roomQ.Where(room.ID.In(contactRoomIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询房间失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// // 收集单聊房间的id
// roomFriendIdList := make([]int64, 0)
// // 收集群聊房间的id
// roomGroupIdList := make([]int64, 0)
// for _, room := range roomRList {
// if room.Type == enum.PERSONAL {
// roomFriendIdList = append(roomFriendIdList, room.ID)
// } else if room.Type == enum.GROUP {
// roomGroupIdList = append(roomGroupIdList, room.ID)
// }
// }
//
// // 查询好友房间信息
// roomFriend := global.Query.RoomFriend
// roomFriendQ := roomFriend.WithContext(ctx)
// roomFriendRList, err := roomFriendQ.Where(roomFriend.RoomID.In(roomFriendIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询好友房间失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// // 查询用户信息
// uidList := make([]int64, 0)
// for _, roomFriend := range roomFriendRList {
// if roomFriend.Uid1 == uid {
// uidList = append(uidList, roomFriend.Uid2)
// } else {
// uidList = append(uidList, roomFriend.Uid1)
// }
// }
// user := global.Query.User
// userQ := user.WithContext(ctx)
// userRList, err := userQ.Where(user.ID.In(uidList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询群聊房间信息
// roomGroup := global.Query.RoomGroup
// roomGroupQ := roomGroup.WithContext(ctx)
// roomGroupRList, err := roomGroupQ.Where(roomGroup.RoomID.In(roomGroupIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询群聊房间失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询最后一条消息
// lastMsgIdList := make([]int64, 0)
// for _, contact := range *contactList {
// lastMsgIdList = append(lastMsgIdList, contact.LastMsgID)
// }
// msg := global.Query.Message
// msgQ := msg.WithContext(ctx)
// msgRList, err := msgQ.Where(msg.ID.In(lastMsgIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询最后一条消息失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询未读消息数
// //fixme: 有没有更好的方法
// countMap := cmap.New[int64]()
// var wg sync.WaitGroup
// err = nil
// for i, contact := range *contactList {
// wg.Add(1)
// go func() {
//
// defer wg.Done()
// var count int64
// count, err = msgQ.Where(msg.RoomID.Eq(contact.RoomID), msg.DeleteStatus.Eq(pkgEnum.NORMAL), msg.CreateTime.Gt(contact.ReadTime)).Limit(99).Count()
// if err != nil {
// global.Logger.Errorf("统计未读数失败 %s", err)
// }
// countMap.Set(strconv.FormatInt(contact.RoomID, 10), count)
//
// }()
// if i == 2 {
// break
// }
//
// }
// wg.Wait()
// if err != nil {
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 拼装结果
// contactDaoList := adapter.BuildContactDaoList(*contactList, userRList, msgRList, roomRList, roomFriendRList, roomGroupRList, countMap)
//
// pageResp.Data = contactDaoList
// return pkgResp.SuccessResponseData(pageResp), nil
//}
//
//// FIXME: 合并GetNewContactListService和GetContactListService
//func GetNewContactListService(uid int64, timestamp int64) (pkgResp.ResponseData, error) {
// contactTime := time.Unix(0, timestamp*1000*1000)
//
// ctx := context.Background()
// contact := global.Query.Contact
// contactQ := contact.WithContext(ctx)
// contactRList, err := contactQ.Where(contact.UID.Eq(uid), contact.ActiveTime.Gte(contactTime)).Find()
// if err != nil {
// global.Logger.Errorf("查询会话列表失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 收集会话id
// contactRoomIdList := make([]int64, 0)
// for _, contact := range contactRList {
// contactRoomIdList = append(contactRoomIdList, contact.RoomID)
// }
//
// // 查询出对应的房间信息
// room := global.Query.Room
// roomQ := room.WithContext(ctx)
// // 查询房间类型
// roomRList, err := roomQ.Where(room.ID.In(contactRoomIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询房间失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// // 收集单聊房间的id
// roomFriendIdList := make([]int64, 0)
// // 收集群聊房间的id
// roomGroupIdList := make([]int64, 0)
// for _, room := range roomRList {
// if room.Type == enum.PERSONAL {
// roomFriendIdList = append(roomFriendIdList, room.ID)
// } else if room.Type == enum.GROUP {
// roomGroupIdList = append(roomGroupIdList, room.ID)
// }
// }
//
// // 查询好友房间信息
// roomFriend := global.Query.RoomFriend
// roomFriendQ := roomFriend.WithContext(ctx)
// roomFriendRList, err := roomFriendQ.Where(roomFriend.RoomID.In(roomFriendIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询好友房间失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// // 查询用户信息
// uidList := make([]int64, 0)
// for _, roomFriend := range roomFriendRList {
// if roomFriend.Uid1 == uid {
// uidList = append(uidList, roomFriend.Uid2)
// } else {
// uidList = append(uidList, roomFriend.Uid1)
// }
// }
// user := global.Query.User
// userQ := user.WithContext(ctx)
// userRList, err := userQ.Where(user.ID.In(uidList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询群聊房间信息
// roomGroup := global.Query.RoomGroup
// roomGroupQ := roomGroup.WithContext(ctx)
// roomGroupRList, err := roomGroupQ.Where(roomGroup.RoomID.In(roomGroupIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询群聊房间失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询最后一条消息
// lastMsgIdList := make([]int64, 0)
// for _, contact := range contactRList {
// lastMsgIdList = append(lastMsgIdList, contact.LastMsgID)
// }
// msg := global.Query.Message
// msgQ := msg.WithContext(ctx)
// msgRList, err := msgQ.Where(msg.ID.In(lastMsgIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询最后一条消息失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询未读消息数
// //fixme: 有没有更好的方法
// countMap := cmap.New[int64]()
// var wg sync.WaitGroup
// err = nil
// for _, contact := range contactRList {
// wg.Add(1)
// go func() {
// defer wg.Done()
// var count int64
// count, err = msgQ.Where(msg.RoomID.Eq(contact.RoomID), msg.DeleteStatus.Eq(pkgEnum.NORMAL), msg.CreateTime.Gt(contact.ReadTime)).Limit(99).Count()
// if err != nil {
// global.Logger.Errorf("统计未读数失败 %s", err)
// }
// countMap.Set(strconv.FormatInt(contact.RoomID, 10), count)
// }()
// }
// wg.Wait()
// if err != nil {
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 类型转换
// temp := make([]model.Contact, 0)
// for _, contact := range contactRList {
// temp = append(temp, *contact)
// }
//
// // 拼装结果
// contactDaoList := adapter.BuildContactDaoList(temp, userRList, msgRList, roomRList, roomFriendRList, roomGroupRList, countMap)
//
// return pkgResp.SuccessResponseData(contactDaoList), nil
//}
//
//func GetContactDetailService(c *gin.Context) {
// uid := c.GetInt64("uid")
// getMessageListReq := req.GetMessageListReq{}
// if err := c.ShouldBindQuery(&getMessageListReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// roomId := getMessageListReq.RoomId
// cursor, err := timestampToTime(getMessageListReq.Cursor)
// if err != nil {
// global.Logger.Errorf("时间戳转换失败 %s", err)
// resp.ErrorResponse(c, "系统正忙,请稍后再试")
// c.Abort()
// return
// }
// pageRequest := pkgReq.PageReq{
// Cursor: cursor,
// PageSize: getMessageListReq.PageSize,
// }
// // 更新会话表
// contact := global.Query.Contact
// contactQ := contact.WithContext(context.Background())
// _, err = contactQ.Where(contact.UID.Eq(uid), contact.RoomID.Eq(roomId)).Update(contact.ReadTime, time.Now())
// if err != nil {
// global.Logger.Errorf("更新会话失败 %s", err)
// resp.ErrorResponse(c, "系统正忙,请稍后再试")
// c.Abort()
// return
// }
//
// // 获取会话详情
// pageResp, err := GetContactDetail(roomId, pageRequest)
// if err != nil {
// global.Logger.Errorf("查询会话详情失败 %s", err)
// resp.ErrorResponse(c, "系统正忙,请稍后再试")
// c.Abort()
// return
// }
// resp.SuccessResponse(c, pageResp)
// return
//}
//
//func GetContactDetail(roomID int64, pageRequest pkgReq.PageReq) (*pkgResp.PageResp, error) {
// // 查询消息
// db := dal.DB
// msgs := make([]model.Message, 0)
// condition := []interface{}{"room_id=? AND delete_status=?", strconv.FormatInt(roomID, 10), pkgEnum.NORMAL}
// pageResp, err := utils.Paginate(db, pageRequest, &msgs, "create_time", false, condition...)
// if err != nil {
// global.Logger.Errorf("查询消息失败: %s", err.Error())
// return nil, err
// }
// msgList := *(pageResp.Data.(*[]model.Message))
// for i, j := 0, len(msgList)-1; i < j; i, j = i+1, j-1 {
// msgList[i], msgList[j] = msgList[j], msgList[i]
// }
// userIdMap := make(map[int64]*int64)
// for _, msg := range msgList {
// if userIdMap[msg.FromUID] == nil {
// userIdMap[msg.FromUID] = &msg.FromUID
// }
// }
// // 转换成列表
// userIdList := make([]int64, 0)
// for _, uid := range userIdMap {
// userIdList = append(userIdList, *uid)
// }
// // 查询用户信息
// ctx := context.Background()
// user := global.Query.User
// userQ := user.WithContext(ctx)
// users, err := userQ.Where(user.ID.In(userIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户失败: %s", err.Error())
// return nil, err
// }
// userMap := make(map[int64]*model.User)
// for _, user := range users {
// userMap[user.ID] = user
// }
//
// // 拼装结果
// pageResp.Data = adapter.BuildMessageRespByMsgAndUser(&msgList, userMap)
// return pageResp, nil
//}
//func GetNewMsgService(msgId int64, roomId int64) (pkgResp.ResponseData, error) {
// ctx := context.Background()
// // 查询消息
// msg := global.Query.Message
// msgQ := msg.WithContext(ctx)
// msgRList, err := msgQ.Where(msg.ID.Gt(msgId), msg.RoomID.Eq(roomId)).Find()
// if err != nil {
// global.Logger.Errorf("查询消息失败: %s", err.Error())
// return pkgResp.ErrorResponseData("接收消息失败"), err
// }
//
// userIdMap := make(map[int64]*int64)
// for _, msg := range msgRList {
// if userIdMap[msg.FromUID] == nil {
// userIdMap[msg.FromUID] = &msg.FromUID
// }
// }
//
// // 转换成列表
// userIdList := make([]int64, 0)
// for _, uid := range userIdMap {
// userIdList = append(userIdList, *uid)
// }
// // 查询用户信息
// user := global.Query.User
// userQ := user.WithContext(ctx)
// users, err := userQ.Where(user.ID.In(userIdList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户失败: %s", err.Error())
// return pkgResp.ErrorResponseData("接收消息失败"), err
// }
// userMap := make(map[int64]*model.User)
// for _, user := range users {
// userMap[user.ID] = user
// }
//
// temp := make([]model.Message, 0)
// for _, msg := range msgRList {
// temp = append(temp, *msg)
// }
//
// // 拼装结果
// data := adapter.BuildMessageRespByMsgAndUser(&temp, userMap)
// return pkgResp.SuccessResponseData(data), nil
//}
//
//func timestampToTime(timestampStr *string) (*string, error) {
// if timestampStr != nil && *timestampStr != "" && *timestampStr != "null" {
// // 时间戳转时间
// timestamp, err := strconv.ParseInt(*timestampStr, 10, 64)
// if err != nil {
// global.Logger.Errorf("时间戳转换失败 %s", err)
// return nil, err
// }
// cursor := time.Unix(0, timestamp)
// cursorStr := cursor.Format(time.RFC3339Nano)
// return &cursorStr, nil
// }
// return nil, nil
//}
//
//func GetUserInfoBatchService(reqList req.GetUserInfoBatchReq) (pkgResp.ResponseData, error) {
// ctx := context.Background()
// user := global.Query.User
// userQ := user.WithContext(ctx)
// uids := make([]int64, 0)
//
// userMap := make(map[int64]*req.UserInfoBatchReqItem)
// for _, item := range reqList.List {
// uids = append(uids, item.Uid)
// userMap[item.Uid] = &item
// }
// users, err := userQ.Where(user.ID.In(uids...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户失败 %s", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// resultList := make([]domainResp.GetUserInfoBatchResp, 0)
// for _, user := range users {
// resultItem := domainResp.GetUserInfoBatchResp{
// Uid: user.ID,
// }
// if user.UpdateTime.UnixMilli() > userMap[user.ID].LastModifyTime {
// resultItem.Username = user.Name
// resultItem.Avatar = user.Avatar
// resultItem.NeedRefresh = true
// } else {
// resultItem.NeedRefresh = false
// }
// resultList = append(resultList, resultItem)
// }
//
// return pkgResp.SuccessResponseData(resultList), nil
//
//}
================================================
FILE: service/friend_service.go
================================================
package service
//
//import (
// "DiTing-Go/dal"
// "DiTing-Go/dal/model"
// "DiTing-Go/domain/dto"
// domainEnum "DiTing-Go/domain/enum"
// "DiTing-Go/domain/vo/req"
// domainResp "DiTing-Go/domain/vo/resp"
// "DiTing-Go/global"
// "DiTing-Go/pkg/domain/enum"
// pkgReq "DiTing-Go/pkg/domain/vo/req"
// "DiTing-Go/pkg/domain/vo/resp"
// "DiTing-Go/pkg/utils"
// "DiTing-Go/utils/jsonUtils"
// "DiTing-Go/utils/redisCache"
// "context"
// "fmt"
// "github.com/pkg/errors"
// "gorm.io/gen"
// "gorm.io/gorm"
// "sort"
// "strconv"
// "time"
//)
//
//// ApplyFriendService 添加好友
//func ApplyFriendService(uid int64, applyReq req.UserApplyReq) (resp.ResponseData, error) {
// ctx := context.Background()
// friendUid := applyReq.Uid
// user := global.Query.User
// userQ := user.WithContext(ctx)
//
// uids := utils.Int64Slice{uid, friendUid}
// sort.Sort(uids)
// key := fmt.Sprintf(domainEnum.UserAndFriendLock, uids[0], uids[1])
// mutex, err := utils.GetLock(key)
// if err != nil {
// return resp.ErrorResponseData("系统正忙,请稍后再试"), err
// }
// defer utils.ReleaseLock(mutex)
//
// //检查用户是否存在
// fun := func() (interface{}, error) {
// return userQ.Where(user.ID.Eq(friendUid)).First()
// }
// userR := model.User{}
// key = fmt.Sprintf(domainEnum.UserCacheByID, applyReq.Uid)
// err = utils.GetData(key, &userR, fun)
// if err != nil {
// if errors.Is(err, gorm.ErrRecordNotFound) {
// return resp.ErrorResponseData("用户不存在"), errors.New("Business Error")
// }
// global.Logger.Errorf("查询用户失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// // 检查是否已经是好友关系
// isFriend, err := IsFriend(uid, friendUid)
// if err != nil {
// global.Logger.Errorf("查询好友失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// // 已经是好友
// if isFriend {
// return resp.ErrorResponseData("已经是好友"), errors.New("Business Error")
// }
// // 检查是否已经发送过好友请求
// userApply := global.Query.UserApply
// userApplyQ := userApply.WithContext(ctx)
// userApplyR := model.UserApply{}
// fun = func() (interface{}, error) {
// return userApplyQ.Where(userApply.UID.Eq(uid), userApply.TargetID.Eq(friendUid)).First()
// }
// key = fmt.Sprintf(domainEnum.UserApplyCacheByUidAndFriendUid, uid, friendUid)
// err = utils.GetData(key, &userApplyR, fun)
// // 查到了
// if err == nil {
// return resp.ErrorResponseData("已发送过好友请求,请等待对方同意"), errors.New("Business Error")
// }
// if !errors.Is(err, gorm.ErrRecordNotFound) {
// global.Logger.Errorf("查询好友请求失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// // 检查对方是否给我们发送过好友请求,如果是,直接同意
// fun = func() (interface{}, error) {
// return userApplyQ.Where(userApply.UID.Eq(friendUid), userApply.TargetID.Eq(uid)).First()
// }
// key = fmt.Sprintf(domainEnum.UserApplyCacheByUidAndFriendUid, friendUid, uid)
// err = utils.GetData(key, &userApplyR, fun)
// // 查到了
// if err == nil {
// err := AgreeFriend(uid, friendUid)
// if err != nil {
// global.Logger.Errorf("同意好友请求失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// return resp.SuccessResponseData(nil), nil
// }
// if !errors.Is(err, gorm.ErrRecordNotFound) {
// global.Logger.Errorf("查询好友请求失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// // 发送好友请求
// err = userApplyQ.Create(&model.UserApply{
// UID: uid,
// TargetID: friendUid,
// Msg: applyReq.Msg,
// Status: enum.NO,
// ReadStatus: enum.NO,
// })
// if err != nil {
// global.Logger.Errorf("插入好友请求失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// // 发送好友申请事件
// err = jsonUtils.SendMsgSync(domainEnum.FriendApplyTopic, model.UserApply{
// UID: uid,
// TargetID: friendUid,
// Msg: applyReq.Msg,
// Status: enum.NO,
// ReadStatus: enum.NO,
// })
// if err != nil {
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// //time.Sleep(30 * time.Second)
// return resp.SuccessResponseData(nil), nil
//}
//
//// IsFriendService 判断是否是好友
//func IsFriendService(uid, friendUid int64) (resp.ResponseData, error) {
// isFriend, err := IsFriend(uid, friendUid)
// if err != nil {
// global.Logger.Errorf("判断好友关系失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// return resp.SuccessResponseData(isFriend), nil
//}
//
//// IsFriend 判断是否是好友
//func IsFriend(uid, friendUid int64) (bool, error) {
// ctx := context.Background()
// userFriend := global.Query.UserFriend
// userFriendQ := userFriend.WithContext(ctx)
// // 检查是否已经是好友关系
// userFriendR := model.UserFriend{}
// fun := func() (interface{}, error) {
// return userFriendQ.Where(userFriend.UID.Eq(uid), userFriend.FriendUID.Eq(friendUid), userFriend.DeleteStatus.Eq(enum.NORMAL)).First()
// }
// key := fmt.Sprintf(domainEnum.UserFriendCacheByUidAndFriendUid, uid, friendUid)
// err := utils.GetData(key, &userFriendR, fun)
// if err != nil {
// // 没查到,不是好友
// if errors.Is(err, gorm.ErrRecordNotFound) {
// return false, nil
// }
// global.Logger.Errorf("查询好友失败 %s", err)
// return false, err
// }
//
// return true, nil
//}
//
//func AgreeFriendService(uid, friendUid int64) (resp.ResponseData, error) {
//
// uids := utils.Int64Slice{uid, friendUid}
// sort.Sort(uids)
// key := fmt.Sprintf(domainEnum.UserAndFriendLock, uids[0], uids[1])
// mutex, err := utils.GetLock(key)
// if err != nil {
// return resp.ErrorResponseData("系统正忙,请稍后再试"), err
// }
// defer utils.ReleaseLock(mutex)
//
// err = AgreeFriend(uid, friendUid)
// if err != nil {
// global.Logger.Errorf("同意好友请求失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// return resp.SuccessResponseData(nil), nil
//}
//
//// AgreeFriend 同意好友请求
//func AgreeFriend(uid, friendUid int64) error {
// ctx := context.Background()
// userApply := global.Query.UserApply
// userApplyQ := userApply.WithContext(ctx)
//
// // 检查是否存在好友申请且状态为待审批
// fun := func() (interface{}, error) {
// return userApplyQ.Where(userApply.UID.Eq(friendUid), userApply.TargetID.Eq(uid)).First()
// }
// userApplyR := model.UserApply{}
// key := fmt.Sprintf(domainEnum.UserApplyCacheByUidAndFriendUid, friendUid, uid)
// err := utils.GetData(key, &userApplyR, fun)
// if err != nil {
// return err
// }
// // 好友申请状态不是待审批
// if userApplyR.Status != enum.NO {
// return errors.New("error status")
// }
// // 同意好友请求
// userApplyR.Status = enum.YES
// // 事务
// tx := q.Begin()
// userApplyTx := tx.UserApply.WithContext(context.Background())
// userFriendTx := tx.UserFriend.WithContext(context.Background())
// if _, err = userApplyTx.Where(userApply.UID.Eq(friendUid), userApply.TargetID.Eq(uid)).Updates(userApplyR); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// return err
// }
// defer utils.RemoveData(key)
//
// var userFriends = []*model.UserFriend{
// {
// UID: uid,
// FriendUID: friendUid,
// DeleteStatus: enum.NORMAL,
// },
// {
// UID: friendUid,
// FriendUID: uid,
// DeleteStatus: enum.NORMAL,
// },
// }
// // 检查是否存在软删除状态的好友关系
// userFriend := global.Query.UserFriend
// fun = func() (interface{}, error) {
// return userFriendTx.Where(userFriend.UID.Eq(uid), userFriend.FriendUID.Eq(friendUid), userFriend.DeleteStatus.Eq(enum.DELETED)).First()
// }
// userFriendR := model.UserFriend{}
// key = fmt.Sprintf(domainEnum.UserFriendCacheByUidAndFriendUid, uid, friendUid)
// err = utils.GetData(key, &userFriendR, fun)
// // err
// if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
// global.Logger.Errorf("更新好友关系失败 %s", err.Error())
// return err
// }
// // 查到了,更新状态
// if err == nil {
// _, err := userFriendTx.Where(userFriend.UID.Eq(uid), userFriend.FriendUID.Eq(friendUid)).Update(userFriend.DeleteStatus, enum.NORMAL)
// _, err = userFriendTx.Where(userFriend.UID.Eq(friendUid), userFriend.FriendUID.Eq(uid)).Update(userFriend.DeleteStatus, enum.NORMAL)
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("更新好友关系失败 %s", err.Error())
// return err
// }
// // 删除redis缓存
// key = fmt.Sprintf(domainEnum.UserFriendCacheByUidAndFriendUid, uid, friendUid)
// defer utils.RemoveData(key)
// key = fmt.Sprintf(domainEnum.UserFriendCacheByUidAndFriendUid, friendUid, uid)
// defer utils.RemoveData(key)
// } else {
// // 没查到,创建新的好友关系
// if err = userFriendTx.Create(userFriends...); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err)
// }
// return err
// }
// }
// if err := tx.Commit(); err != nil {
// return err
// }
//
// // 发送新好友事件
// err = jsonUtils.SendMsgSync(domainEnum.NewFriendTopic, userFriends[0])
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("发送新好友事件失败 %s", err.Error())
// return err
// }
// return nil
//}
//
//// DeleteFriendService 删除好友
//// 只删除好友关系和会话,其他耗时操作异步处理
//func DeleteFriendService(uid int64, deleteFriendReq req.DeleteFriendReq) (resp.ResponseData, error) {
// ctx := context.Background()
//
// deleteFriendUid := deleteFriendReq.Uid
//
// uids := utils.Int64Slice{uid, deleteFriendUid}
// sort.Sort(uids)
// key := fmt.Sprintf(domainEnum.UserAndFriendLock, uids[0], uids[1])
// mutex, err := utils.GetLock(key)
// if err != nil {
// return resp.ErrorResponseData("系统正忙,请稍后再试"), err
// }
// defer utils.ReleaseLock(mutex)
//
// // 判断是否为好友
// isFriend, err := IsFriend(uid, deleteFriendUid)
// if err != nil {
// global.Logger.Errorf("查询好友关系失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// if !isFriend {
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
//
// tx := global.Query.Begin()
// // 事务
// // 软删除好友关系
// userFriend := global.Query.UserFriend
// userFriendTx := tx.UserFriend.WithContext(ctx)
// if _, err := userFriendTx.Where(userFriend.UID.Eq(uid), userFriend.FriendUID.Eq(deleteFriendUid)).Update(userFriend.DeleteStatus, enum.DELETED); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("删除好友失败 %s", err.Error())
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
// // 删除redis缓存
// defer redisCache.RemoveUserFriend(uid, deleteFriendUid)
//
// if _, err := userFriendTx.Where(userFriend.UID.Eq(deleteFriendUid), userFriend.FriendUID.Eq(uid)).Update(userFriend.DeleteStatus, enum.DELETED); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("删除好友失败 %s", err.Error())
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
// // 删除redis缓存
// defer redisCache.RemoveUserFriend(deleteFriendUid, uid)
//
// // 删除会话
// roomFriend := global.Query.RoomFriend
// roomFriendTx := tx.RoomFriend.WithContext(ctx)
// uids = utils.Int64Slice{uid, deleteFriendUid}
// sort.Sort(uids)
// fun := func() (interface{}, error) {
// return roomFriendTx.Where(roomFriend.Uid1.Eq(uids[0]), roomFriend.Uid2.Eq(uids[1])).First()
// }
// roomFriendR := model.RoomFriend{}
// key = fmt.Sprintf(domainEnum.RoomFriendCacheByUidAndFriendUid, uids[0], uids[1])
// if err := utils.GetData(key, &roomFriendR, fun); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("查询好友房间失败 %s", err.Error())
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
//
// contact := global.Query.Contact
// contactTx := tx.Contact.WithContext(ctx)
// resultInfo, err := contactTx.Where(contact.RoomID.Eq(roomFriendR.RoomID)).Delete()
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("删除会话失败 %s", err.Error())
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
// if resultInfo.RowsAffected == 0 {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("会话不存在 %d", roomFriendR.RoomID)
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
// // TODO:删除缓存
//
// // 发送消息
// DeleteFriendDto := dto.DeleteFriendDto{
// Uid: uid,
// FriendUid: deleteFriendUid,
// }
// err = jsonUtils.SendMsgSync(domainEnum.DeleteFriendTopic, DeleteFriendDto)
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("发送删除好友事件失败 %s", err.Error())
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// if err := tx.Commit(); err != nil {
// global.Logger.Errorf("事务提交失败 %s", err.Error())
// return resp.ErrorResponseData("删除好友失败"), errors.New("Business Error")
// }
//
// return resp.SuccessResponseData(nil), nil
//}
//
//// GetUserApplyService 获取好友申请列表
//func GetUserApplyService(uid int64, pageReq pkgReq.PageReq) (resp.ResponseData, error) {
// if pageReq.Cursor != nil && *pageReq.Cursor != "" {
// // 时间戳转时间
// timestamp, err := strconv.ParseInt(*pageReq.Cursor, 10, 64)
// if err != nil {
// global.Logger.Errorf("时间戳转换失败 %s", err)
// return resp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// cursor := time.Unix(0, timestamp)
// cursorStr := cursor.Format(time.RFC3339Nano)
// pageReq.Cursor = &cursorStr
// }
//
// ctx := context.Background()
// // 获取 UserApply 表中 TargetID 等于 uid(登录用户ID)的用户ID集合,采用游标分页
// db := dal.DB
// userApplys := make([]model.UserApply, 0)
// condition := []interface{}{"target_id=?", strconv.FormatInt(uid, 10)}
//
// pageResp, err := utils.Paginate(db, pageReq, &userApplys, "create_time", false, condition...)
// if err != nil {
// global.Logger.Errorf("查询好友申请表失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// uids := make([]int64, 0)
// n := len(userApplys)
// for i := 0; i < n; i++ {
// uids = append(uids, userApplys[i].UID)
// }
//
// user := global.Query.User
// // 根据 uids 集合查询 User 表
// users, err := user.WithContext(ctx).Select(user.ID, user.Name, user.Avatar).Where(user.ID.In(uids...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户表失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// if len(users) != len(userApplys) {
// global.Logger.Errorf("用户表和好友申请表数据不匹配")
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// var usersVO = make([]domainResp.UserApplyResp, 0)
// // 数据转换
// for i := 0; i < len(users); i++ {
// var userVO domainResp.UserApplyResp
// userVO.ApplyId = userApplys[i].ID
// userVO.Uid = userApplys[i].UID
// userVO.Msg = userApplys[i].Msg
// userVO.Status = userApplys[i].Status
// usersVO = append(usersVO, userVO)
// }
// pageResp.Data = usersVO
//
// userApply := global.Query.UserApply
// userApplyQ := global.Query.UserApply.WithContext(ctx)
// // 更新已读状态
// _, err = userApplyQ.Where(userApply.TargetID.Eq(uid), userApply.ReadStatus.Eq(enum.NO)).Update(userApply.ReadStatus, enum.YES)
// if err != nil {
// global.Logger.Errorf("更新好友申请表失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
//
// return resp.SuccessResponseData(pageResp), nil
//}
//
//func UnreadApplyNumService(uid int64) (resp.ResponseData, error) {
// ctx := context.Background()
// userApply := global.Query.UserApply
// userApplyQ := userApply.WithContext(ctx)
//
// // TODO 直接count
// // 获取 UserApply 表中 TargetID 等于 uid(登录用户ID)的用户ID集合
// subQuery := userApplyQ.Where(userApply.TargetID.Eq(uid), userApply.ReadStatus.Eq(enum.NO)).Limit(99)
// num, err := gen.Table(subQuery.As("t")).Count()
// if err != nil {
// global.Logger.Errorf("查询好友申请表失败 %s", err)
// return resp.ErrorResponseData("系统正忙,请稍后再试"), errors.New("Business Error")
// }
// return resp.SuccessResponseData(num), nil
//}
//
//// GetFriendListService 获取好友列表
//func GetFriendListService(uid int64, pageReq pkgReq.PageReq) (resp.ResponseData, error) {
// if pageReq.Cursor != nil && *pageReq.Cursor != "" {
// // 时间戳转时间
// timestamp, err := strconv.ParseInt(*pageReq.Cursor, 10, 64)
// if err != nil {
// global.Logger.Errorf("时间戳转换失败 %s", err)
// return resp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// cursor := time.Unix(0, timestamp)
// cursorStr := cursor.Format(time.RFC3339Nano)
// pageReq.Cursor = &cursorStr
// }
//
// ctx := context.Background()
// // 获取 UserFriend 表中 uid = uid 的好友的uid组成的集合
// db := dal.DB
// userFriend := make([]model.UserFriend, 0)
// condition := []interface{}{"uid=? and delete_status=?", strconv.FormatInt(uid, 10), enum.NORMAL}
// pageResp, err := utils.Paginate(db, pageReq, &userFriend, "create_time", false, condition...)
// if err != nil {
// global.Logger.Errorf("分页查询失败 %v", err)
// return resp.ErrorResponseData("系统繁忙,请稍后再试"), errors.New("Business Error")
// }
// uids := make([]int64, 0)
//
// for _, friend := range userFriend {
// uids = append(uids, friend.FriendUID)
// }
//
// // 获取好友信息
// users := global.Query.User
// // select id , name , avatar from user where id in (...) and status = 0
// friendList, err := users.WithContext(ctx).Select(users.ID, users.ActiveStatus, users.LastOptTime).Where(users.ID.In(uids...)).Find()
// if err != nil {
// global.Logger.Errorf("查询用户表失败 %s", err)
// return resp.ErrorResponseData("系统繁忙,请稍后再试"), errors.New("Business Error")
// }
//
// // 数据转换
// friendListVO := make([]domainResp.UserContactResp, 0)
// for _, friend := range friendList {
// friendResp := domainResp.UserContactResp{
// Uid: friend.ID,
// LastOptTime: friend.LastOptTime.UnixMilli(),
// ActiveStatus: int(friend.ActiveStatus),
// }
// friendListVO = append(friendListVO, friendResp)
// }
// pageResp.Data = friendListVO
// return resp.SuccessResponseData(pageResp), nil
//}
================================================
FILE: service/group_service.go
================================================
package service
//
//import (
// "DiTing-Go/dal/model"
// "DiTing-Go/domain/dto"
// "DiTing-Go/domain/enum"
// "DiTing-Go/domain/vo/req"
// "DiTing-Go/global"
// pkgEnum "DiTing-Go/pkg/domain/enum"
// "DiTing-Go/pkg/domain/vo/resp"
// pkgResp "DiTing-Go/pkg/domain/vo/resp"
// "context"
// "fmt"
// "github.com/gin-gonic/gin"
// "github.com/pkg/errors"
// "gorm.io/gorm"
// "strconv"
// "strings"
// "time"
//)
//
//func CreateGroupService(uid int64, uidList []int64) (pkgResp.ResponseData, error) {
//
// tx := global.Query.Begin()
// ctx := context.Background()
// // 创建房间表
// roomTx := tx.Room.WithContext(ctx)
// newRoom := model.Room{
// Type: enum.GROUP,
// ExtJSON: "{}",
// }
// if err := roomTx.Create(&newRoom); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("添加房间表失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 查询用户头像
// user := global.Query.User
// userTx := tx.User.WithContext(ctx)
// userR, err := userTx.Where(user.ID.Eq(uid)).First()
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("查询用户表失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// uidList = append([]int64{uid}, uidList...)
// userRList, err := userTx.Where(user.ID.In(uidList...)).Find()
// groupName := ""
// for _, user := range userRList {
// groupName += (user.Name + "、")
// }
// groupName = strings.TrimRight(groupName, "、")
// runes := []rune(groupName)
// if len(runes) > 10 {
// runes = runes[:10]
// }
// groupName = string(runes) + "..."
//
// // 创建群聊表
// roomGroupTx := tx.RoomGroup.WithContext(ctx)
// newRoomGroup := model.RoomGroup{
// RoomID: newRoom.ID,
// Name: groupName,
// // 默认为创建者头像
// Avatar: userR.Avatar,
// ExtJSON: "{}",
// }
// if err := roomGroupTx.Create(&newRoomGroup); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("添加群聊表失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// groupMemberTx := tx.GroupMember.WithContext(ctx)
// newGroupMemberList := []*model.GroupMember{
// {
// UID: uid,
// GroupID: newRoomGroup.ID,
// // TODO: 1为群主,抽取为常量
// Role: 1,
// },
// }
// for _, userInfo := range userRList {
// newGroupMemberList = append(newGroupMemberList, &model.GroupMember{
// UID: userInfo.ID,
// GroupID: newRoomGroup.ID,
// // TODO: 1为群主,抽取为常量
// Role: 2,
// })
// }
// if err := groupMemberTx.Create(newGroupMemberList...); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("添加群组成员表失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// // 自动发送一条消息
// messageTx := tx.Message.WithContext(ctx)
// newMessage := model.Message{
// FromUID: uid,
// RoomID: newRoom.ID,
// Type: enum.TextMessageType,
// Content: "欢迎加入群聊",
// Extra: "{}",
// DeleteStatus: pkgEnum.NORMAL,
// }
// if err := messageTx.Create(&newMessage); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("添加消息表失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// // 创建会话表
// contactTx := tx.Contact.WithContext(ctx)
// newContact := model.Contact{
// UID: uid,
// RoomID: newRoom.ID,
// ReadTime: time.Now(),
// ActiveTime: time.Now(),
// LastMsgID: newMessage.ID,
// }
// if err := contactTx.Create(&newContact); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// }
// global.Logger.Errorf("添加会话表失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// if err := tx.Commit(); err != nil {
// global.Logger.Errorf("事务提交失败 %s", err.Error())
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
//
// global.Bus.Publish(enum.NewMessageEvent, newMessage)
//
// return pkgResp.SuccessResponseData("success"), nil
//}
//
//// DeleteGroupService 删除群聊
////
//// @Summary 删除群聊
//// @Produce json
//// @Param id body string true "房间ID"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/group/:id [delete]
//func DeleteGroupService(c *gin.Context) {
// uid := c.GetInt64("uid")
// deleteGroupReq := req.DeleteGroupReq{}
// if err := c.ShouldBindUri(&deleteGroupReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
//
// tx := global.Query.Begin()
// defer func() {
//
// }()
// ctx := context.Background()
// // 查询群聊id
// roomGroup := global.Query.RoomGroup
// roomGroupTx := tx.RoomGroup.WithContext(ctx)
// roomGroupR, err := roomGroupTx.Where(roomGroup.RoomID.Eq(deleteGroupReq.ID)).First()
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("查询群聊表失败 %s", err.Error())
// return
//
// }
// // TODO:查询用户是否在群聊中
// groupMember := global.Query.GroupMember
// groupMemberTx := tx.GroupMember.WithContext(ctx)
// // 查询用户是否是群主
// _, err = groupMemberTx.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(roomGroupR.ID), groupMember.Role.Eq(1)).First()
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("查询群组成员表失败 %s", err.Error())
// return
// }
// // 获取群聊成员
// groupMemberList, err := groupMemberTx.Where(groupMember.GroupID.Eq(roomGroupR.ID)).Find()
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("查询群组成员表失败 %s", err.Error())
// return
// }
//
// // 删除所有成员的会话表
// for _, groupMember := range groupMemberList {
// contact := global.Query.Contact
// contactTx := tx.Contact.WithContext(ctx)
// if _, err := contactTx.Where(contact.UID.Eq(groupMember.UID), contact.RoomID.Eq(roomGroupR.ID)).Delete(); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("删除会话表失败 %s", err.Error())
// return
// }
// }
//
// // 删除群聊表
// if _, err := roomGroupTx.Where(roomGroup.RoomID.Eq(roomGroupR.ID)).Delete(); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("删除群聊表失败 %s", err.Error())
// return
// }
// // 删除房间表
// room := global.Query.Room
// roomTx := tx.Room.WithContext(ctx)
// if _, err := roomTx.Where(room.ID.Eq(roomGroupR.ID)).Delete(); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("删除房间表失败 %s", err.Error())
// return
// }
// // 删除群组成员表
// if _, err := groupMemberTx.Where(groupMember.GroupID.Eq(roomGroupR.ID)).Delete(); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("删除群组成员表失败 %s", err.Error())
// return
// }
// // TODO:抽取为事件
// // 删除消息表
// message := global.Query.Message
// messageTx := tx.Message.WithContext(ctx)
// msg := model.Message{
// DeleteStatus: 0,
// }
// if _, err := messageTx.Where(message.RoomID.Eq(roomGroupR.ID)).Updates(msg); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// global.Logger.Errorf("删除消息表失败 %s", err.Error())
// return
// }
// // TODO: 删除群聊仅禁止发送新消息,不删除消息
// if err := tx.Commit(); err != nil {
// global.Logger.Errorf("事务提交失败 %s", err.Error())
// resp.ErrorResponse(c, "删除群聊失败")
// c.Abort()
// return
// }
// resp.SuccessResponseWithMsg(c, "success")
// return
//}
//
//// JoinGroupService 加入群聊
////
//// @Summary 加入群聊
//// @Produce json
//// @Param id body int true "房间id"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/group/create [post]
//func JoinGroupService(c *gin.Context) {
// uid := c.GetInt64("uid")
// joinGroupReq := req.JoinGroupReq{}
// if err := c.ShouldBind(&joinGroupReq); err != nil { //ShouldBind()会自动推导
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
// ctx := context.Background()
// // 房间是否存在
// room := global.Query.Room
// roomQ := room.WithContext(ctx)
// roomR, err := roomQ.Where(room.ID.Eq(joinGroupReq.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "加入群聊失败")
// global.Logger.Errorf("查询房间失败 %s", err)
// c.Abort()
// return
// }
// if roomR.Type != enum.GROUP {
// resp.ErrorResponse(c, "加入群聊失败")
// global.Logger.Errorf("房间类型错误 %s", err)
// c.Abort()
// return
// }
// // 是否已经加入群聊
// roomGroup := global.Query.RoomGroup
// roomGroupQ := roomGroup.WithContext(ctx)
// // 查询群聊表
// roomGroupR, err := roomGroupQ.Where(roomGroup.RoomID.Eq(roomR.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "加入群聊失败")
// global.Logger.Errorf("查询群聊失败 %s", err)
// c.Abort()
// return
// }
//
// groupMember := global.Query.GroupMember
// groupMemberQ := groupMember.WithContext(ctx)
// groupMemberR, err := groupMemberQ.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(roomGroupR.ID)).First()
// if err != nil && err.Error() != "record not found" {
// resp.ErrorResponse(c, "加入群聊失败")
// global.Logger.Errorf("查询群成员表失败 %s", err)
// c.Abort()
// return
// }
// if groupMemberR != nil {
// resp.ErrorResponse(c, "禁止重复加入群聊")
// c.Abort()
// return
// }
//
// // 加入群聊
// tx := global.Query.Begin()
// groupMemberTx := tx.GroupMember.WithContext(ctx)
// newGroupMember := model.GroupMember{
// UID: uid,
// GroupID: roomGroupR.ID,
// // 普通成员
// Role: 3,
// }
// if err := groupMemberTx.Create(&newGroupMember); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "加入群聊失败")
// c.Abort()
// global.Logger.Errorf("添加群组成员表失败 %s", err.Error())
// return
// }
// // 创建会话表
// contactTx := tx.Contact.WithContext(ctx)
// newContact := model.Contact{
// UID: uid,
// RoomID: roomR.ID,
// }
// if err := contactTx.Create(&newContact); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "加入群聊失败")
// c.Abort()
// global.Logger.Errorf("添加会话表失败 %s", err.Error())
// return
// }
//
// // 自动发送一条消息
// messageTx := tx.Message.WithContext(ctx)
// newMessage := model.Message{
// FromUID: uid,
// RoomID: roomR.ID,
// Type: enum.TextMessageType,
// Content: "大家好~",
// Extra: "{}",
// }
// if err := messageTx.Create(&newMessage); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err.Error())
// return
// }
// resp.ErrorResponse(c, "加入群聊失败")
// c.Abort()
// global.Logger.Errorf("添加消息表失败 %s", err.Error())
// return
// }
// if err := tx.Commit(); err != nil {
// global.Logger.Errorf("事务提交失败 %s", err.Error())
// resp.ErrorResponse(c, "加入群聊失败")
// c.Abort()
// return
// }
// global.Bus.Publish(enum.NewMessageEvent, newMessage)
//
// resp.SuccessResponseWithMsg(c, "success")
//}
//
//// QuitGroupService 退出群聊
////
//// @Summary 退出群聊
//// @Produce json
//// @Param id body int true "房间id"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/group/create [post]
//func QuitGroupService(c *gin.Context) {
// uid := c.GetInt64("uid")
// quitGroupReq := req.QuitGroupReq{}
// if err := c.ShouldBind(&quitGroupReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
//
// ctx := context.Background()
// tx := global.Query.Begin()
// // 群聊是否存在
// room := global.Query.Room
// roomTx := tx.Room.WithContext(ctx)
// _, err := roomTx.Where(room.ID.Eq(quitGroupReq.ID)).First()
// if err != nil {
// if err.Error() != gorm.ErrRecordNotFound.Error() {
// global.Logger.Errorf("查询房间失败 %s", err)
// }
// resp.ErrorResponse(c, "群聊不存在")
// c.Abort()
// return
// }
// // 用户是否在群聊中
// groupMember := global.Query.GroupMember
// groupMemberTx := tx.GroupMember.WithContext(ctx)
// _, err = groupMemberTx.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(quitGroupReq.ID)).First()
// if err != nil {
// if err.Error() == gorm.ErrRecordNotFound.Error() {
// resp.ErrorResponse(c, "未加入群聊")
// c.Abort()
// return
// }
// resp.ErrorResponse(c, "退出群聊失败")
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// c.Abort()
// return
// }
// // 删除会话表
// contact := global.Query.Contact
// contactTx := tx.Contact.WithContext(ctx)
// if _, err := contactTx.Where(contact.UID.Eq(uid), contact.RoomID.Eq(quitGroupReq.ID)).Delete(); err != nil {
// resp.ErrorResponse(c, "退出群聊失败")
// global.Logger.Errorf("删除会话表失败 %s", err)
// c.Abort()
// return
// }
// // 删除群组成员表
// // 查询群组
// roomGroup := global.Query.RoomGroup
// roomGroupTx := tx.RoomGroup.WithContext(ctx)
// roomGroupR, err := roomGroupTx.Where(roomGroup.RoomID.Eq(quitGroupReq.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "退出群聊失败")
// global.Logger.Errorf("查询群聊失败 %s", err)
// c.Abort()
// return
// }
// if _, err := groupMemberTx.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(roomGroupR.ID)).Delete(); err != nil {
// resp.ErrorResponse(c, "退出群聊失败")
// global.Logger.Errorf("删除群组成员表失败 %s", err)
// c.Abort()
// return
// }
//
// if err := tx.Commit(); err != nil {
// global.Logger.Errorf("事务提交失败 %s", err)
// resp.ErrorResponse(c, "退出群聊失败")
// c.Abort()
// return
// }
// resp.SuccessResponseWithMsg(c, "success")
// return
//}
//
//// GetGroupMemberListService 退出群聊
////
//// @Summary 退出群聊
//// @Produce json
//// @Param id body int true "房间id"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/group/getGroupMemberList [get]
//func GetGroupMemberListService(c *gin.Context) {
// uid := c.GetInt64("uid")
// getGroupMemberListReq := req.GetGroupMemberListReq{}
// if err := c.ShouldBindQuery(&getGroupMemberListReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
// ctx := context.Background()
//
// // 查询房间表
// room := global.Query.Room
// roomQ := room.WithContext(ctx)
// roomR, err := roomQ.Where(room.ID.Eq(getGroupMemberListReq.RoomId)).First()
// if err != nil {
// resp.ErrorResponse(c, "查询群聊失败")
// global.Logger.Errorf("查询房间失败 %s", err)
// c.Abort()
// return
// }
// // 查询群聊表
// roomGroup := global.Query.RoomGroup
// roomGroupQ := roomGroup.WithContext(ctx)
// roomGroupR, err := roomGroupQ.Where(roomGroup.RoomID.Eq(roomR.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "查询群聊失败")
// global.Logger.Errorf("查询群聊失败 %s", err)
// c.Abort()
// return
// }
//
// // 查询用户是否在群聊中
// groupMember := global.Query.GroupMember
// groupMemberQ := groupMember.WithContext(ctx)
// _, err = groupMemberQ.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(roomGroupR.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "查询群聊失败")
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// c.Abort()
// return
// }
//
// // 分页查询
// // 默认值
// // 划分游标,status_activateTime
// var userR []dto.GetGroupMemberDto
// status, activeTime := cursorSplit(getGroupMemberListReq.Cursor)
// // 查询群组成员表,联表游标翻页
// user := global.Query.User
// userQ := user.WithContext(ctx)
// if err := userQ.Select(user.ID, user.Name, user.Avatar, user.ActiveStatus, user.LastOptTime).LeftJoin(groupMemberQ, user.ID.EqCol(groupMember.UID)).Where(groupMember.GroupID.Eq(roomGroupR.ID), user.ActiveStatus.Eq(int32(status)), user.LastOptTime.Gt(activeTime)).Limit(getGroupMemberListReq.PageSize).Scan(&userR); err != nil {
// resp.ErrorResponse(c, "查询群聊失败")
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// c.Abort()
// return
// }
// // 不足,用不在线的补充
// if len(userR) < getGroupMemberListReq.PageSize && status == 1 {
// var add []dto.GetGroupMemberDto
// if err := userQ.Select(user.ID, user.Name, user.Avatar, user.ActiveStatus, user.LastOptTime).LeftJoin(groupMemberQ, user.ID.EqCol(groupMember.UID)).Where(groupMember.GroupID.Eq(roomGroupR.ID), user.ActiveStatus.Eq(2)).Limit(getGroupMemberListReq.PageSize - len(userR)).Scan(&add); err != nil {
// resp.ErrorResponse(c, "查询群聊失败")
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// c.Abort()
// return
// }
// userR = append(userR, add...)
// }
//
// newCursor := genCursor(userR)
// resp.SuccessResponse(c, pkgResp.PageResp{
// Cursor: &newCursor,
// IsLast: len(userR) < getGroupMemberListReq.PageSize,
// Data: userR,
// })
//}
//
//// GrantAdministratorService 授予管理员权限
////
//// @Summary 授予管理员权限
//// @Produce json
//// @Param room_id body int true "房间id"
//// @Param grant_uid body int true "授权用户id"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/group/getGroupMemberList [get]
//func GrantAdministratorService(c *gin.Context) {
// uid := c.GetInt64("uid")
// grantAdministratorReq := req.GrantAdministratorReq{}
// if err := c.ShouldBind(&grantAdministratorReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
// ctx := context.Background()
// // 检查用户是否为群主
// roomGroup := global.Query.RoomGroup
// roomGroupQ := roomGroup.WithContext(ctx)
// roomGroupR, err := roomGroupQ.Where(roomGroup.RoomID.Eq(grantAdministratorReq.RoomId)).First()
// if err != nil {
// resp.ErrorResponse(c, "授权失败")
// global.Logger.Errorf("查询群聊失败 %s", err)
// c.Abort()
// return
// }
// groupMember := global.Query.GroupMember
// groupMemberQ := groupMember.WithContext(ctx)
// groupMemberR, err := groupMemberQ.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(roomGroupR.ID)).First()
// if err != nil {
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// resp.ErrorResponse(c, "授权失败")
// c.Abort()
// return
// }
// if groupMemberR.Role != 1 {
// resp.ErrorResponse(c, "授权失败,权限不足")
// c.Abort()
// return
// }
//
// // 检查授权用户是否在群聊中
// groupMemberR, err = groupMemberQ.Where(groupMember.UID.Eq(grantAdministratorReq.GrantUid), groupMember.GroupID.Eq(roomGroupR.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "授权失败,用户不在群聊中")
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// c.Abort()
// return
// }
// // 如果用户是不是普通用户
// if groupMemberR.Role != 3 {
// resp.ErrorResponse(c, "授权失败")
// c.Abort()
// return
// }
// // 授权
// groupMemberR.Role = 2
// groupMemberR.UpdateTime = time.Now()
// if _, err := groupMemberQ.Where(groupMember.ID.Eq(groupMemberR.ID)).Updates(groupMemberR); err != nil {
// resp.ErrorResponse(c, "授权失败")
// global.Logger.Errorf("更新群组成员表失败 %s", err)
// c.Abort()
// return
// }
//
// resp.SuccessResponseWithMsg(c, "success")
// return
//}
//
//// RemoveAdministratorService 移除管理员权限
////
//// @Summary 移除管理员权限
//// @Produce json
//// @Param room_id body int true "房间id"
//// @Param remove_uid body int true "授权用户id"
//// @Success 200 {object} resp.ResponseData "成功"
//// @Failure 500 {object} resp.ResponseData "内部错误"
//// @Router /api/group/getGroupMemberList [get]
//func RemoveAdministratorService(c *gin.Context) {
// uid := c.GetInt64("uid")
// removeAdministratorReq := req.RemoveAdministratorReq{}
// if err := c.ShouldBind(&removeAdministratorReq); err != nil {
// resp.ErrorResponse(c, "参数错误")
// global.Logger.Errorf("参数错误: %v", err)
// c.Abort()
// return
// }
// ctx := context.Background()
// // 检查用户是否为群主
// roomGroup := global.Query.RoomGroup
// roomGroupQ := roomGroup.WithContext(ctx)
// roomGroupR, err := roomGroupQ.Where(roomGroup.RoomID.Eq(removeAdministratorReq.RoomId)).First()
// if err != nil {
// resp.ErrorResponse(c, "移除管理员失败")
// global.Logger.Errorf("查询群聊失败 %s", err)
// c.Abort()
// return
// }
// groupMember := global.Query.GroupMember
// groupMemberQ := groupMember.WithContext(ctx)
// groupMemberR, err := groupMemberQ.Where(groupMember.UID.Eq(uid), groupMember.GroupID.Eq(roomGroupR.ID)).First()
// if err != nil {
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// resp.ErrorResponse(c, "移除管理员失败")
// c.Abort()
// return
// }
// if groupMemberR.Role != 1 {
// resp.ErrorResponse(c, "移除管理员失败,权限不足")
// c.Abort()
// return
// }
//
// // 检查授权用户是否在群聊中
// groupMemberR, err = groupMemberQ.Where(groupMember.UID.Eq(removeAdministratorReq.RemoveUid), groupMember.GroupID.Eq(roomGroupR.ID)).First()
// if err != nil {
// resp.ErrorResponse(c, "移除管理员失败,用户不在群聊中")
// global.Logger.Errorf("查询群组成员表失败 %s", err)
// c.Abort()
// return
// }
// // 如果用户是不是普通用户
// if groupMemberR.Role != 2 {
// resp.ErrorResponse(c, "移除管理员失败")
// c.Abort()
// return
// }
//
// // 移除权限
// groupMemberR.Role = 3
// groupMemberR.UpdateTime = time.Now()
// if _, err := groupMemberQ.Where(groupMember.ID.Eq(groupMemberR.ID)).Updates(groupMemberR); err != nil {
// resp.ErrorResponse(c, "移除管理员失败")
// global.Logger.Errorf("更新群组成员表失败 %s", err)
// c.Abort()
// return
// }
//
// resp.SuccessResponseWithMsg(c, "success")
// return
//}
//
//// 分割游标
//func cursorSplit(cursor *string) (int, time.Time) {
// if cursor == nil {
// return 1, time.Time{}
// }
// // TODO: 抽取为常量
// lines := strings.Split(*cursor, "_")
// status, err := strconv.Atoi(lines[0])
// if err != nil {
// return 1, time.Time{}
// }
// timeUnix, err := strconv.ParseInt(lines[1], 10, 64)
// if err != nil {
// return 1, time.Time{}
// }
// activeTime := time.Unix(timeUnix, timeUnix%1000000000)
// return status, activeTime
//}
//
//// 生成游标
//func genCursor(users []dto.GetGroupMemberDto) string {
// if users == nil || len(users) == 0 {
// return fmt.Sprintf("%d_%d", 2, time.Now().Unix())
// }
// status := users[len(users)-1].ActiveStatus
// activeTime := users[len(users)-1].LastOptTime
// newCursor := fmt.Sprintf("%d_%d", status, activeTime.UnixMilli())
// return newCursor
//}
================================================
FILE: service/message_service.go
================================================
package service
//
//import (
// "DiTing-Go/dal/model"
// "DiTing-Go/domain/enum"
// "DiTing-Go/domain/vo/req"
// domainResp "DiTing-Go/domain/vo/resp"
// "DiTing-Go/global"
// pkgEnum "DiTing-Go/pkg/domain/enum"
// "DiTing-Go/pkg/domain/vo/resp"
// "context"
// "github.com/apache/rocketmq-client-go/v2/primitive"
// "github.com/goccy/go-json"
// "log"
// "time"
//)
//
//// SendTextMsgService 发送文本消息
//func SendTextMsgService(uid int64, msgReq req.MessageReq) (resp.ResponseData, error) {
// ctx := context.Background()
// user := global.Query.User
// userQ := user.WithContext(ctx)
// userR, err := userQ.Where(user.ID.Eq(uid)).First()
// if err != nil {
// log.Println("查询用户失败", err)
// return resp.ErrorResponseData("消息发送失败"), err
// }
//
// msg := model.Message{}
// msg.Type = msgReq.MsgType
// msg.FromUID = uid
// msg.RoomID = msgReq.RoomId
// msg.Content = msgReq.Body.Content
// if msg.Extra == "" {
// msg.Extra = "{}"
// }
//
// // 发送消息
// if err := SendTextMsg(&msg); err != nil {
// return resp.ErrorResponseData("消息发送失败"), err
// }
// // 发送新消息事件
// newMsgByte, _ := json.Marshal(msg)
// rMsg := &primitive.Message{
// Topic: enum.NewMessageTopic,
// Body: newMsgByte,
// }
// _, _ = global.RocketProducer.SendSync(ctx, rMsg)
//
// msgResp := domainResp.MessageResp{
// FromUser: domainResp.MsgUser{
// Uid: uid,
// Username: userR.Name,
// Avatar: userR.Avatar,
// },
// SendTime: msg.CreateTime.UnixMilli(),
// Message: domainResp.Msg{
// ID: msg.ID,
// RoomId: msg.RoomID,
// Type: msg.Type,
// Body: domainResp.TextBody{
// Content: msg.Content,
// Reply: msg.ReplyMsgID,
// },
// },
// }
//
// // 返回成功
// return resp.SuccessResponseData(msgResp), nil
//}
//
//func SendTextMsg(msg *model.Message) error {
// msg.CreateTime = time.Now()
// msg.DeleteStatus = pkgEnum.NORMAL
// ctx := context.Background()
// msgQ := global.Query.WithContext(ctx).Message
// if err := msgQ.Create(msg); err != nil {
// log.Println("消息发送失败", err.Error())
// return err
// }
// return nil
//}
================================================
FILE: service/upload_service.go
================================================
package service
//
//import (
// "DiTing-Go/dal/model"
// "DiTing-Go/domain/dto"
// "DiTing-Go/domain/enum"
// voResp "DiTing-Go/domain/vo/resp"
// "DiTing-Go/global"
// "DiTing-Go/pkg/domain/vo/resp"
// "context"
// "fmt"
// "github.com/gin-gonic/gin"
// "github.com/goccy/go-json"
// "github.com/minio/minio-go/v7"
// "strconv"
// "time"
//)
//
//// GetPreSigned 签发url
//func GetPreSigned(c *gin.Context) {
// uid := c.GetInt64("uid")
// ctx := context.Background()
// roomIdStr, found := c.GetQuery("roomId")
// if !found {
// global.Logger.Errorf("参数错误 %s", roomIdStr)
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// roomId, err := strconv.ParseInt(roomIdStr, 10, 64)
// if err != nil {
// global.Logger.Errorf("参数错误 %s", roomId)
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
//
// fileName, found := c.GetQuery("fileName")
// if !found {
// global.Logger.Errorf("参数错误 %s", roomIdStr)
// resp.ErrorResponse(c, "参数错误")
// c.Abort()
// return
// }
// // 构造文件名:time+uid+filename
// // 按天创建桶
// timeStr := time.Now().Format("2006-01-02")
// fileName = fmt.Sprintf("%s/%d/%s", timeStr, uid, fileName)
//
// policy := minio.NewPostPolicy()
// // TODO:抽象为常量
// if err := policy.SetBucket("diting"); err != nil {
// global.Logger.Errorf("创建policy失败 %s", roomIdStr)
// resp.ErrorResponse(c, "获取签名失败,请稍后再试")
// c.Abort()
// return
// }
// if err := policy.SetKey(fileName); err != nil {
// global.Logger.Errorf("创建policy失败 %s", roomIdStr)
// resp.ErrorResponse(c, "获取签名失败,请稍后再试")
// c.Abort()
// return
// }
// // 失效时间1天
// if err := policy.SetExpires(time.Now().UTC().AddDate(0, 0, 1)); err != nil {
// global.Logger.Errorf("创建policy失败 %s", roomIdStr)
// resp.ErrorResponse(c, "获取签名失败,请稍后再试")
// c.Abort()
// return
// }
// url, formData, err := global.MinioClient.PresignedPostPolicy(ctx, policy)
// if err != nil {
// global.Logger.Errorf("创建policy失败 %s", roomIdStr)
// resp.ErrorResponse(c, "获取签名失败,请稍后再试")
// c.Abort()
// return
// }
// preSignedResp := voResp.PreSignedResp{
// Url: url.String(),
// Policy: formData,
// }
// tx := global.Query.Begin()
// // 插入消息表
// messageTx := tx.Message.WithContext(ctx)
// base := dto.MessageBaseDto{
// Url: url.String(),
// Size: -1,
// Name: fileName,
// }
// extra := dto.ImgMessageDto{
// MessageBaseDto: base,
// // TODO: 宽高需要前端传
// Width: -1,
// Height: -1,
// }
// jsonStr, err := json.Marshal(extra)
// if err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err)
// }
// global.Logger.Errorf("json序列化失败 %s", err)
// resp.ErrorResponse(c, "获取签名失败,请稍后再试")
// c.Abort()
// return
// }
// // TODO:抽象为常量
// newMsg := model.Message{
// FromUID: uid,
// RoomID: roomId,
// Content: "[图片]",
// DeleteStatus: 0,
// Type: 3,
// Extra: string(jsonStr),
// }
// if err := messageTx.Create(&newMsg); err != nil {
// if err := tx.Rollback(); err != nil {
// global.Logger.Errorf("事务回滚失败 %s", err)
// }
// global.Logger.Errorf("数据库插入失败 %s", err)
// resp.ErrorResponse(c, "获取签名失败,请稍后再试")
// c.Abort()
// return
// }
//
// global.Bus.Publish(enum.NewMessageEvent, newMsg)
//
// resp.SuccessResponse(c, preSignedResp)
// return
//}
================================================
FILE: service/user_service.go
================================================
package service
import (
"DiTing-Go/dal/model"
domainEnum "DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"DiTing-Go/domain/vo/resp"
"DiTing-Go/global"
"DiTing-Go/logic"
pkgResp "DiTing-Go/pkg/domain/vo/resp"
"DiTing-Go/utils"
"DiTing-Go/utils/jwt"
"context"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis"
"github.com/pkg/errors"
)
// RegisterService 用户注册
func RegisterService(userReq req.UserRegisterReq) (pkgResp.ResponseData, error) {
ctx := context.Background()
// 参数校验
if err := validateRegisterRequest(userReq); err != nil {
return pkgResp.ErrorResponseData(err.Error()), errors.New("Business Error")
}
// 验证验证码
if err := validateRegisterCaptcha(userReq.Phone, userReq.Captcha); err != nil {
return pkgResp.ErrorResponseData(err.Error()), errors.New("Business Error")
}
// 检查用户是否已存在
exists, err := checkUserExists(ctx, userReq.Phone)
if err != nil {
global.Logger.Errorf("检查用户是否存在失败: phone=%s, err=%v", userReq.Phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
if exists {
return pkgResp.ErrorResponseData("手机号已存在"), errors.New("Business Error")
}
// 创建用户
if err := createNewUser(ctx, userReq); err != nil {
global.Logger.Errorf("创建用户失败: phone=%s, err=%v", userReq.Phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
return pkgResp.SuccessResponseDataWithMsg("注册成功"), nil
}
// validateRegisterRequest 验证注册请求参数
func validateRegisterRequest(userReq req.UserRegisterReq) error {
if userReq.Username == "" || userReq.Password == "" || userReq.Phone == "" {
global.Logger.Infof("用户名、密码和手机号不能为空: userReq=%v", userReq)
return errors.New("用户名、密码和手机号不能为空")
}
if userReq.Captcha == "" {
global.Logger.Infof("验证码不能为空: phone=%s", userReq.Phone)
return errors.New("验证码不能为空")
}
// 验证密码强度
if len(userReq.Password) < 6 {
return errors.New("密码长度不能少于6位")
}
// 验证手机号格式(简单验证)
if len(userReq.Phone) != 11 {
return errors.New("手机号格式不正确")
}
return nil
}
// validateRegisterCaptcha 验证注册验证码
func validateRegisterCaptcha(phone, captcha string) error {
if !logic.CheckCaptchaProcess(phone, captcha) {
global.Logger.Infof("验证码错误: phone=%s", phone)
return errors.New("验证码错误")
}
return nil
}
// checkUserExists 检查用户是否已存在
func checkUserExists(ctx context.Context, phone string) (bool, error) {
// 先检查Redis缓存
if logic.CheckPhoneInRedis(phone) {
global.Logger.Infof("手机号已存在: phone=%s", phone)
return true, nil
}
// 检查数据库
exists, err := logic.CheckPhoneInDB(ctx, phone)
if err != nil {
global.Logger.Errorf("检查用户是否存在失败: phone=%s, err=%v", phone, err)
return false, err
}
if exists {
global.Logger.Infof("手机号已存在: phone=%s", phone)
return true, nil
}
return false, nil
}
// createNewUser 创建新用户
func createNewUser(ctx context.Context, userReq req.UserRegisterReq) error {
// 对密码进行md5加密
password := utils.EncryptPassword(userReq.Password)
// 创建用户对象
newUser := model.User{
Name: userReq.Username,
Password: password,
Phone: userReq.Phone,
IPInfo: "{}",
}
// 创建用户
if err := logic.CreateUser(ctx, newUser); err != nil {
global.Logger.Errorf("创建用户失败: phone=%s, err=%v", userReq.Phone, err)
return err
}
// 缓存用户信息到Redis
if err := logic.SetUserInfo2Redis(newUser); err != nil {
global.Logger.Errorf("缓存用户信息失败: userId=%d, err=%v", newUser.ID, err)
// 不返回错误,因为用户创建成功,缓存失败不影响注册流程
}
// 缓存用户ID映射
userPhoneKey := utils.MakeUserPhoneKey(userReq.Phone)
if err := utils.SetValueToRedis(userPhoneKey, fmt.Sprintf("%d", newUser.ID), domainEnum.NotExpireTime); err != nil {
global.Logger.Errorf("缓存用户ID映射失败: userId=%d, err=%v", newUser.ID, err)
// 不返回错误,因为用户创建成功,缓存失败不影响注册流程
}
global.Logger.Infof("用户注册成功: phone=%s, userId=%d", userReq.Phone, newUser.ID)
return nil
}
// LoginService 用户登录
func LoginService(loginReq req.UserLoginReq) (pkgResp.ResponseData, error) {
ctx := context.Background()
// 参数校验
if err := validateLoginRequest(loginReq); err != nil {
return pkgResp.ErrorResponseData(err.Error()), errors.New("Business Error")
}
// 验证登录凭据
if err := validateLoginCredentials(ctx, loginReq); err != nil {
return pkgResp.ErrorResponseData(err.Error()), errors.New("Business Error")
}
// 获取用户信息
user, err := getUserInfo(loginReq.Phone)
if err != nil {
global.Logger.Errorf("获取用户信息失败: phone=%s, err=%v", loginReq.Phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
// 生成JWT token
token, err := jwt.GenerateToken(user.ID)
if err != nil {
global.Logger.Errorf("生成token失败: userId=%d, err=%v", user.ID, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
// 构建响应
userResp := resp.UserLoginResp{
Token: token,
Uid: user.ID,
Name: user.Name,
Avatar: user.Avatar,
}
return pkgResp.SuccessResponseData(userResp), nil
}
// validateLoginRequest 验证登录请求参数
func validateLoginRequest(loginReq req.UserLoginReq) error {
if loginReq.LoginType == domainEnum.LoginByPassword {
if loginReq.Phone == "" || loginReq.Password == "" {
global.Logger.Infof("用户名和密码不能为空: loginReq=%v", loginReq)
return errors.New("用户名和密码不能为空")
}
} else {
if loginReq.Phone == "" || loginReq.Captcha == "" {
global.Logger.Infof("手机号和验证码不能为空: loginReq=%v", loginReq)
return errors.New("手机号和验证码不能为空")
}
}
return nil
}
// validateLoginCredentials 验证登录凭据
func validateLoginCredentials(ctx context.Context, loginReq req.UserLoginReq) error {
if loginReq.LoginType == domainEnum.LoginByPassword {
if !logic.CheckPassword(ctx, loginReq.Phone, loginReq.Password) {
global.Logger.Infof("用户名或密码错误: phone=%s", loginReq.Phone)
return errors.New("用户名或密码错误")
}
} else {
if !logic.CheckCaptchaProcess(loginReq.Phone, loginReq.Captcha) {
global.Logger.Infof("验证码错误: phone=%s", loginReq.Phone)
return errors.New("验证码错误")
}
}
return nil
}
// getUserInfo 获取用户信息,优先从Redis获取,失败则从数据库获取并缓存
func getUserInfo(phone string) (model.User, error) {
userPhoneKey := utils.MakeUserPhoneKey(phone)
// 从Redis获取用户ID
userIdByte, err := utils.GetValueFromRedis(userPhoneKey)
var userId int64
json.Unmarshal(userIdByte, &userId)
if err != nil && !errors.Is(err, redis.Nil) {
global.Logger.Errorf("从Redis获取用户ID失败: userPhoneKey=%s, err=%v", userPhoneKey, err)
return model.User{}, err
}
// 如果Redis中有用户ID,尝试获取用户信息
if userId != 0 {
user, err := logic.GetUserInfo2Redis(fmt.Sprintf("%d", userId))
if err == nil {
return user, nil
}
// Redis中用户信息不存在或出错,继续从数据库获取
global.Logger.Infof("从Redis获取用户信息失败,尝试从数据库获取: userId=%s, err=%v", userId, err)
}
// 从数据库获取用户信息
user, err := logic.GetUserInfo2DB(phone)
if err != nil {
global.Logger.Errorf("从数据库获取用户信息失败: phone=%s, err=%v", phone, err)
return model.User{}, err
}
// 将用户信息缓存到Redis
if err := logic.SetUserInfo2Redis(user); err != nil {
global.Logger.Errorf("缓存用户信息到Redis失败: userId=%d, err=%v", user.ID, err)
}
// 缓存用户ID映射
if err := utils.SetValueToRedis(userPhoneKey, fmt.Sprintf("%d", user.ID), domainEnum.NotExpireTime); err != nil {
global.Logger.Errorf("缓存用户ID映射失败: userId=%d, err=%v", user.ID, err)
}
return user, nil
}
// CancelService 注销账户
func CancelService(ctx *gin.Context, req req.UserCancelReq) (pkgResp.ResponseData, error) {
// 获取用户信息
userId, exists := ctx.Get("uid")
if !exists {
global.Logger.Errorf("用户id不存在")
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
userIdNum, ok := userId.(int64)
userIdStr := fmt.Sprintf("%d", userIdNum)
if !ok {
global.Logger.Errorf("获取用户ID失败: userId=%v", userId)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
userInfo, err := logic.GetUserInfo2DBById(ctx, userIdStr)
if err != nil {
global.Logger.Errorf("获取用户信息失败: userId=%s, err=%v", userId, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
phone := userInfo.Phone
captcha := req.Captcha
// 检查验证码是否正确
if !logic.CheckCaptchaProcess(phone, captcha) {
global.Logger.Infof("验证码错误: phone=%s", phone)
return pkgResp.ErrorResponseData("验证码错误"), errors.New("验证码错误")
}
// 检查用户是否存在
userExists, err := checkUserExists(ctx, phone)
if err != nil {
global.Logger.Errorf("检查用户是否存在失败: phone=%s, err=%v", phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
if !userExists {
global.Logger.Errorf("用户不存在: phone=%s", phone)
return pkgResp.ErrorResponseData("用户不存在"), errors.New("Business Error")
}
// 删除用户缓存
if err := logic.DeleteUserInfoFromRedis(userIdStr); err != nil {
global.Logger.Errorf("删除用户缓存失败: phone=%s, err=%v", phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
// 删除用户ID映射
userPhoneKey := utils.MakeUserPhoneKey(phone)
if err := utils.DeleteValueFromRedis(userPhoneKey); err != nil {
global.Logger.Errorf("删除用户ID映射失败: phone=%s, err=%v", phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
// 删除用户数据库
if err := logic.DeleteUserInfoFromDB(ctx, userIdStr); err != nil {
global.Logger.Errorf("删除用户数据库失败: phone=%s, err=%v", phone, err)
return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
}
return pkgResp.SuccessResponseDataWithMsg("注销成功"), nil
}
//
//func GetUserInfoByNameService(uid int64, name string) (pkgResp.ResponseData, error) {
// ctx := context.Background()
// user := global.Query.User
// userQ := user.WithContext(ctx)
// userRList, err := userQ.Where(user.Name.Like(name + "%")).Limit(5).Find()
// if err != nil {
// global.Logger.Errorf("查询用户数据失败: %v", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// uidList := make([]int64, 0)
// for _, userR := range userRList {
// uidList = append(uidList, userR.ID)
// }
// // 搜索好友关系
// userApply := global.Query.UserApply
// userApplyQ := userApply.WithContext(ctx)
// applyList, err := userApplyQ.Where(userApply.UID.Eq(uid), userApply.TargetID.In(uidList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询好友关系失败: %v", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// // 查询好友关系
// userFriend := global.Query.UserFriend
// userFriendQ := userFriend.WithContext(ctx)
// friendList, err := userFriendQ.Where(userFriend.UID.Eq(uid), userFriend.FriendUID.In(uidList...)).Find()
// if err != nil {
// global.Logger.Errorf("查询好友关系失败: %v", err)
// return pkgResp.ErrorResponseData("系统繁忙,请稍后再试~"), errors.New("Business Error")
// }
// userRespList := adapter.BuildUserInfoByNameResp(userRList, applyList, friendList)
// return pkgResp.SuccessResponseData(userRespList), nil
//}
================================================
FILE: service/user_service_integration_test.go
================================================
package service
import (
"DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"DiTing-Go/logic"
"DiTing-Go/utils"
"context"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
// TestUserRegistrationFlow 测试用户注册流程
func TestUserRegistrationFlow(t *testing.T) {
// 测试数据
testPhone := "13800138001"
testUsername := "testuser1"
testPassword := "123456"
testCaptcha := "123456"
// 设置验证码
setupCaptcha(testPhone, testCaptcha)
// 执行注册
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
assert.Contains(t, resp.Message, "注册成功")
// 验证用户是否真的被创建
userExists, err := logic.CheckPhoneInDB(context.Background(), testPhone)
assert.NoError(t, err)
assert.True(t, userExists)
// 清理测试数据
cleanupUser(testPhone)
}
// TestUserLoginFlow 测试用户登录流程
func TestUserLoginFlow(t *testing.T) {
// 先注册一个用户
testPhone := "13800138002"
testUsername := "testuser2"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
registerResp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
// 测试密码登录
loginReq := req.UserLoginReq{
Phone: testPhone,
Password: testPassword,
LoginType: enum.LoginByPassword,
}
loginResp, err := LoginService(loginReq)
assert.NoError(t, err)
assert.True(t, loginResp.Success)
// 验证登录响应数据结构
loginData, ok := loginResp.Data.(map[string]interface{})
assert.True(t, ok)
assert.NotEmpty(t, loginData["token"])
assert.NotZero(t, loginData["uid"])
assert.Equal(t, testUsername, loginData["name"])
// 清理测试数据
cleanupUser(testPhone)
}
// TestUserCancelFlow 测试用户注销流程
func TestUserCancelFlow(t *testing.T) {
// 先注册一个用户
testPhone := "13800138003"
testUsername := "testuser3"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
registerResp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
// 设置注销验证码
setupCaptcha(testPhone, testCaptcha)
// 执行注销
ctx := &gin.Context{}
ctx.Set("uid", int64(12345))
cancelReq := req.UserCancelReq{
Captcha: testCaptcha,
}
cancelResp, err := CancelService(ctx, cancelReq)
assert.NoError(t, err)
assert.True(t, cancelResp.Success)
assert.Contains(t, cancelResp.Message, "注销成功")
// 验证用户是否真的被删除
userExists, err := logic.CheckPhoneInDB(context.Background(), testPhone)
assert.NoError(t, err)
assert.False(t, userExists)
}
// TestDuplicateRegistration 测试重复注册
func TestDuplicateRegistration(t *testing.T) {
testPhone := "13800138004"
testUsername := "testuser4"
testPassword := "123456"
testCaptcha := "123456"
// 第一次注册
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp1, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp1.Success)
// 第二次注册相同手机号
setupCaptcha(testPhone, testCaptcha)
resp2, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "手机号已存在")
// 清理测试数据
cleanupUser(testPhone)
}
// TestLoginWithWrongCredentials 测试错误凭据登录
func TestLoginWithWrongCredentials(t *testing.T) {
// 先注册一个用户
testPhone := "13800138005"
testUsername := "testuser5"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 测试错误密码
wrongPasswordReq := req.UserLoginReq{
Phone: testPhone,
Password: "wrongpassword",
LoginType: enum.LoginByPassword,
}
resp2, err := LoginService(wrongPasswordReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "用户名或密码错误")
// 测试错误验证码
wrongCaptchaReq := req.UserLoginReq{
Phone: testPhone,
Captcha: "999999",
LoginType: enum.LoginByPhoneCaptcha,
}
resp3, err := LoginService(wrongCaptchaReq)
assert.NoError(t, err)
assert.False(t, resp3.Success)
assert.Contains(t, resp3.Message, "验证码错误")
// 清理测试数据
cleanupUser(testPhone)
}
// TestCancelWithWrongCaptcha 测试错误验证码注销
func TestCancelWithWrongCaptcha(t *testing.T) {
// 先注册一个用户
testPhone := "13800138006"
testUsername := "testuser6"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 测试错误验证码注销
ctx := &gin.Context{}
ctx.Set("uid", int64(12345))
wrongCaptchaReq := req.UserCancelReq{
Captcha: "999999",
}
resp2, err := CancelService(ctx, wrongCaptchaReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "验证码错误")
// 清理测试数据
cleanupUser(testPhone)
}
// TestUserRegistrationWithValidData 测试有效数据注册
func TestUserRegistrationWithValidData(t *testing.T) {
testCases := []struct {
name string
username string
password string
phone string
captcha string
}{
{
name: "标准注册",
username: "testuser7",
password: "123456",
phone: "13800138007",
captcha: "123456",
},
{
name: "长用户名",
username: "verylongusername123",
password: "123456",
phone: "13800138008",
captcha: "123456",
},
{
name: "复杂密码",
username: "testuser9",
password: "P@ssw0rd123",
phone: "13800138009",
captcha: "123456",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
registerReq := req.UserRegisterReq{
Username: tc.username,
Password: tc.password,
Phone: tc.phone,
Captcha: tc.captcha,
}
setupCaptcha(tc.phone, tc.captcha)
resp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 清理测试数据
cleanupUser(tc.phone)
})
}
}
// setupCaptcha 设置验证码
func setupCaptcha(phone, captcha string) {
captchaKey := utils.MakeUserCaptchaKey(phone)
err := utils.SetValueToRedis(captchaKey, captcha, 5*time.Minute)
if err != nil {
panic(err)
}
}
// cleanupUser 清理用户数据
func cleanupUser(phone string) {
// 这里应该实现清理用户数据的逻辑
// 由于涉及到数据库操作,这里只是占位符
// 实际实现时需要根据具体的数据库操作来清理
}
================================================
FILE: service/user_service_test.go
================================================
package service
import (
"DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
// TestUserLifecycleFlow 测试用户完整生命周期流程
func TestUserLifecycleFlow(t *testing.T) {
// 设置测试环境
gin.SetMode(gin.TestMode)
ctx := &gin.Context{}
ctx.Set("uid", int64(12345))
// 测试数据
testPhone := "13800138000"
testUsername := "testuser"
testPassword := "123456"
testCaptcha := "123456"
// 步骤1: 用户注册
t.Log("=== 步骤1: 用户注册测试 ===")
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
// 设置验证码
setupCaptcha(testPhone, testCaptcha)
registerResp, err := RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
// 步骤2: 用户登录(密码登录)
t.Log("=== 步骤2: 用户密码登录测试 ===")
loginReq := req.UserLoginReq{
Phone: testPhone,
Password: testPassword,
LoginType: enum.LoginByPassword,
}
loginResp, err := LoginService(loginReq)
assert.NoError(t, err)
assert.True(t, loginResp.Success)
// 步骤3: 用户登录(验证码登录)
t.Log("=== 步骤3: 用户验证码登录测试 ===")
loginByCaptchaReq := req.UserLoginReq{
Phone: testPhone,
Captcha: testCaptcha,
LoginType: enum.LoginByPhoneCaptcha,
}
setupCaptcha(testPhone, testCaptcha)
loginByCaptchaResp, err := LoginService(loginByCaptchaReq)
assert.NoError(t, err)
assert.True(t, loginByCaptchaResp.Success)
// 步骤4: 用户注销
t.Log("=== 步骤4: 用户注销测试 ===")
cancelReq := req.UserCancelReq{
Captcha: testCaptcha,
}
setupCaptcha(testPhone, testCaptcha)
cancelResp, err := CancelService(ctx, cancelReq)
assert.NoError(t, err)
assert.True(t, cancelResp.Success)
}
// TestRegisterValidation 测试注册参数验证
func TestRegisterValidation(t *testing.T) {
testCases := []struct {
name string
req req.UserRegisterReq
expected string
}{
{
name: "用户名为空",
req: req.UserRegisterReq{
Username: "",
Password: "123456",
Phone: "13800138001",
Captcha: "123456",
},
expected: "用户名、密码和手机号不能为空",
},
{
name: "密码长度不足",
req: req.UserRegisterReq{
Username: "testuser",
Password: "123",
Phone: "13800138001",
Captcha: "123456",
},
expected: "密码长度不能少于6位",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
resp, err := RegisterService(tc.req)
assert.NoError(t, err)
assert.False(t, resp.Success)
assert.Contains(t, resp.Message, tc.expected)
})
}
}
// TestLoginValidation 测试登录参数验证
func TestLoginValidation(t *testing.T) {
testCases := []struct {
name string
req req.UserLoginReq
expected string
}{
{
name: "密码登录-手机号为空",
req: req.UserLoginReq{
Phone: "",
Password: "123456",
LoginType: enum.LoginByPassword,
},
expected: "用户名和密码不能为空",
},
{
name: "验证码登录-手机号为空",
req: req.UserLoginReq{
Phone: "",
Captcha: "123456",
LoginType: enum.LoginByPhoneCaptcha,
},
expected: "手机号和验证码不能为空",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
resp, err := LoginService(tc.req)
assert.NoError(t, err)
assert.False(t, resp.Success)
assert.Contains(t, resp.Message, tc.expected)
})
}
}
================================================
FILE: sql/sql.sql
================================================
-- auto-generated definition
create table contact
(
id bigint unsigned auto_increment comment 'id'
primary key,
uid bigint not null comment 'uid',
room_id bigint not null comment '房间id',
read_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '阅读到的时间',
active_time datetime(3) null comment '会话内消息最后更新的时间(只有普通会话需要维护,全员会话不需要维护)',
last_msg_id bigint null comment '会话最新消息id',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间',
constraint uniq_uid_room_id
unique (uid, room_id)
)
comment '会话列表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on contact (create_time);
create index idx_room_id_read_time
on contact (room_id, read_time);
create index idx_update_time
on contact (update_time);
create index idx_user_id_active_time
on contact (uid, active_time);
-- auto-generated definition
create table group_member
(
id bigint unsigned auto_increment comment 'id'
primary key,
group_id bigint not null comment '群主id',
uid bigint not null comment '成员uid',
role int not null comment '成员角色 1群主 2管理员 3普通成员',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间'
)
comment '群成员表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on group_member (create_time);
create index idx_group_id_role
on group_member (group_id, role);
create index idx_update_time
on group_member (update_time);
-- auto-generated definition
create table message
(
id bigint unsigned auto_increment comment 'id'
primary key,
room_id bigint not null comment '会话表id',
from_uid bigint not null comment '消息发送者uid',
content varchar(1024) null comment '消息内容',
reply_msg_id bigint null comment '回复的消息内容',
status int not null comment '消息状态 0正常 1删除',
gap_count int null comment '与回复的消息间隔多少条',
type int default 1 null comment '消息类型 1正常文本 2.撤回消息',
extra json null comment '扩展信息',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间'
)
comment '消息表' collate = utf8mb4_unicode_ci
row_format = DYNAMIC;
create index idx_create_time
on message (create_time);
create index idx_from_uid
on message (from_uid);
create index idx_room_id
on message (room_id);
create index idx_update_time
on message (update_time);
-- auto-generated definition
create table room
(
id bigint unsigned auto_increment comment 'id'
primary key,
type int not null comment '房间类型 1群聊 2单聊',
hot_flag int default 0 null comment '是否全员展示 0否 1是',
active_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '群最后消息的更新时间(热点群不需要写扩散,只更新这里)',
last_msg_id bigint null comment '会话中的最后一条消息id',
ext_json json null comment '额外信息(根据不同类型房间有不同存储的东西)',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间'
)
comment '房间表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on room (create_time);
create index idx_update_time
on room (update_time);
-- auto-generated definition
create table room_friend
(
id bigint unsigned auto_increment comment 'id'
primary key,
room_id bigint not null comment '房间id',
uid1 bigint not null comment 'uid1(更小的uid)',
uid2 bigint not null comment 'uid2(更大的uid)',
room_key varchar(64) not null comment '房间key由两个uid拼接,先做排序uid1_uid2',
status int not null comment '房间状态 0正常 1禁用(删好友了禁用)',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间',
constraint room_key
unique (room_key)
)
comment '单聊房间表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on room_friend (create_time);
create index idx_room_id
on room_friend (room_id);
create index idx_update_time
on room_friend (update_time);
-- auto-generated definition
create table room_group
(
id bigint unsigned auto_increment comment 'id'
primary key,
room_id bigint not null comment '房间id',
name varchar(16) not null comment '群名称',
avatar varchar(256) not null comment '群头像',
ext_json json null comment '额外信息(根据不同类型房间有不同存储的东西)',
delete_status int(1) default 0 not null comment '逻辑删除(0-正常,1-删除)',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间'
)
comment '群聊房间表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on room_group (create_time);
create index idx_room_id
on room_group (room_id);
create index idx_update_time
on room_group (update_time);
-- auto-generated definition
create table user_friend
(
id bigint unsigned auto_increment comment 'id'
primary key,
uid bigint not null comment 'uid',
friend_uid bigint not null comment '好友uid',
delete_status int(1) default 0 not null comment '逻辑删除(0-正常,1-删除)',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间',
constraint uid
unique (uid, friend_uid)
)
comment '用户联系人表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on user_friend (create_time);
create index idx_uid_friend_uid
on user_friend (uid, friend_uid);
create index idx_update_time
on user_friend (update_time);
-- auto-generated definition
create table user_apply
(
id bigint unsigned auto_increment comment 'id'
primary key,
uid bigint not null comment '申请人uid',
type int not null comment '申请类型 1加好友',
target_id bigint not null comment '接收人uid',
msg varchar(64) not null comment '申请信息',
status int not null comment '申请状态 1待审批 2同意',
read_status int not null comment '阅读状态 1未读 2已读',
create_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '创建时间',
update_time datetime(3) default CURRENT_TIMESTAMP(3) not null on update CURRENT_TIMESTAMP(3) comment '修改时间',
constraint uid
unique (uid, target_id)
)
comment '用户申请表' collate = utf8mb4_unicode_ci;
create index idx_create_time
on user_apply (create_time);
create index idx_target_id
on user_apply (target_id);
create index idx_target_id_read_status
on user_apply (target_id, read_status);
create index idx_uid_target_id
on user_apply (uid, target_id);
create index idx_update_time
on user_apply (update_time);
================================================
FILE: tests/README.md
================================================
# DiTing-Go 测试文件夹结构
## 概述
本文件夹包含了DiTing-Go项目的所有测试文件,按照测试类型和功能模块进行了分类组织。
## 文件夹结构
```
tests/
├── unit/ # 单元测试
├── integration/ # 集成测试
├── e2e/ # 端到端测试
├── performance/ # 性能测试
├── scripts/ # 测试脚本
├── config/ # 测试配置文件
└── README.md # 本文件
```
## 测试类型说明
### 1. 单元测试 (unit/)
- **目的**: 测试单个函数或方法的正确性
- **特点**: 快速、独立、可重复
- **覆盖范围**: 业务逻辑、工具函数、数据验证等
### 2. 集成测试 (integration/)
- **目的**: 测试多个组件之间的协作
- **特点**: 涉及数据库、缓存、外部服务
- **覆盖范围**: 服务层、数据访问层、缓存层
### 3. 端到端测试 (e2e/)
- **目的**: 测试完整的用户业务流程
- **特点**: 模拟真实用户操作
- **覆盖范围**: API接口、WebSocket、完整业务流程
### 4. 性能测试 (performance/)
- **目的**: 测试系统性能和稳定性
- **特点**: 高并发、大数据量、长时间运行
- **覆盖范围**: 负载测试、压力测试、基准测试
## 运行测试
### 运行所有测试
```bash
go test ./tests/...
```
### 运行特定类型测试
```bash
# 运行单元测试
go test ./tests/unit/...
# 运行集成测试
go test ./tests/integration/...
# 运行端到端测试
go test ./tests/e2e/...
# 运行性能测试
go test ./tests/performance/...
```
### 使用测试脚本
```bash
# Linux/Mac
./tests/scripts/run_tests.sh
# Windows
tests\scripts\run_tests.bat
```
## 测试配置
### 环境变量
```bash
# 测试环境
export GIN_MODE=test
export TEST_ENV=test
# 数据库配置
export TEST_DB_HOST=localhost
export TEST_DB_PORT=3306
export TEST_DB_NAME=diting_test
# Redis配置
export TEST_REDIS_HOST=localhost
export TEST_REDIS_PORT=6379
export TEST_REDIS_DB=1
```
## 最佳实践
### 1. 测试编写
- 每个测试函数只测试一个功能点
- 使用描述性的测试函数名
- 包含正向和异常测试用例
### 2. 测试数据
- 使用固定的测试数据确保可重复性
- 避免测试之间的数据依赖
- 及时清理测试数据
### 3. 测试维护
- 定期更新测试用例
- 保持测试代码的可读性
- 及时修复失败的测试
================================================
FILE: tests/e2e/user_workflow_e2e_test.go
================================================
package e2e
import (
"DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"DiTing-Go/global"
"DiTing-Go/service"
"DiTing-Go/utils"
"DiTing-Go/utils/setting"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
// 初始化函数,在包加载时执行
func init() {
// 设置测试环境变量
gin.SetMode(gin.TestMode)
// 初始化配置
setting.ConfigInit()
// 初始化简单的测试日志
global.Logger = logrus.New()
global.Logger.SetOutput(gin.DefaultWriter)
global.Logger.SetLevel(logrus.InfoLevel)
// 初始化Redis
global.RedisInit()
// 初始化数据库
global.DBInit()
}
// TestUserCompleteWorkflow 测试用户完整工作流
func TestUserCompleteWorkflow(t *testing.T) {
// 设置测试环境
gin.SetMode(gin.TestMode)
// 测试数据
testPhone := "13800138100"
testUsername := "e2euser"
testPassword := "123456"
testCaptcha := "123456"
// 步骤1: 用户注册
t.Log("=== E2E测试: 用户注册 ===")
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
registerResp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
t.Log("✅ 用户注册成功")
// 步骤2: 用户登录(密码登录)
t.Log("=== E2E测试: 用户密码登录 ===")
loginReq := req.UserLoginReq{
Phone: testPhone,
Password: testPassword,
LoginType: enum.LoginByPassword,
}
loginResp, err := service.LoginService(loginReq)
assert.NoError(t, err)
assert.True(t, loginResp.Success)
// 验证登录响应
loginData, ok := loginResp.Data.(map[string]interface{})
assert.True(t, ok)
assert.NotEmpty(t, loginData["token"])
assert.NotZero(t, loginData["uid"])
assert.Equal(t, testUsername, loginData["name"])
t.Logf("✅ 用户登录成功: uid=%v, name=%s", loginData["uid"], loginData["name"])
// 步骤3: 用户登录(验证码登录)
t.Log("=== E2E测试: 用户验证码登录 ===")
setupCaptcha(testPhone, testCaptcha)
loginByCaptchaReq := req.UserLoginReq{
Phone: testPhone,
Captcha: testCaptcha,
LoginType: enum.LoginByPhoneCaptcha,
}
loginByCaptchaResp, err := service.LoginService(loginByCaptchaReq)
assert.NoError(t, err)
assert.True(t, loginByCaptchaResp.Success)
t.Log("✅ 验证码登录成功")
// 步骤4: 用户注销
t.Log("=== E2E测试: 用户注销 ===")
ctx := &gin.Context{}
ctx.Set("uid", loginData["uid"])
setupCaptcha(testPhone, testCaptcha)
cancelReq := req.UserCancelReq{
Captcha: testCaptcha,
}
cancelResp, err := service.CancelService(ctx, cancelReq)
assert.NoError(t, err)
assert.True(t, cancelResp.Success)
t.Log("✅ 用户注销成功")
// 步骤5: 验证用户已注销(尝试登录应该失败)
t.Log("=== E2E测试: 验证用户已注销 ===")
loginAfterCancelReq := req.UserLoginReq{
Phone: testPhone,
Password: testPassword,
LoginType: enum.LoginByPassword,
}
loginAfterCancelResp, err := service.LoginService(loginAfterCancelReq)
assert.NoError(t, err)
assert.False(t, loginAfterCancelResp.Success)
t.Log("✅ 验证用户已注销成功")
}
// TestMultipleUserWorkflow 测试多用户并发工作流
func TestMultipleUserWorkflow(t *testing.T) {
// 测试多个用户同时注册和登录
users := []struct {
phone string
username string
password string
}{
{"13800138101", "user1", "123456"},
{"13800138102", "user2", "123456"},
{"13800138103", "user3", "123456"},
}
for i, user := range users {
t.Run(user.username, func(t *testing.T) {
// 注册用户
setupCaptcha(user.phone, "123456")
registerReq := req.UserRegisterReq{
Username: user.username,
Password: user.password,
Phone: user.phone,
Captcha: "123456",
}
registerResp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
// 登录用户
loginReq := req.UserLoginReq{
Phone: user.phone,
Password: user.password,
LoginType: enum.LoginByPassword,
}
loginResp, err := service.LoginService(loginReq)
assert.NoError(t, err)
assert.True(t, loginResp.Success)
t.Logf("✅ 用户 %d 注册和登录成功", i+1)
// 清理测试数据
cleanupUser(user.phone)
})
}
}
// TestUserErrorScenarios 测试用户错误场景
func TestUserErrorScenarios(t *testing.T) {
t.Run("重复注册", func(t *testing.T) {
testPhone := "13800138104"
testUsername := "erroruser"
testPassword := "123456"
// 第一次注册
setupCaptcha(testPhone, "123456")
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: "123456",
}
resp1, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp1.Success)
// 第二次注册相同手机号
setupCaptcha(testPhone, "123456")
resp2, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "手机号已存在")
cleanupUser(testPhone)
})
t.Run("错误密码登录", func(t *testing.T) {
testPhone := "13800138105"
testUsername := "erroruser2"
testPassword := "123456"
// 注册用户
setupCaptcha(testPhone, "123456")
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: "123456",
}
resp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 使用错误密码登录
wrongPasswordReq := req.UserLoginReq{
Phone: testPhone,
Password: "wrongpassword",
LoginType: enum.LoginByPassword,
}
loginResp, err := service.LoginService(wrongPasswordReq)
assert.NoError(t, err)
assert.False(t, loginResp.Success)
assert.Contains(t, loginResp.Message, "用户名或密码错误")
cleanupUser(testPhone)
})
}
// setupCaptcha 设置验证码
func setupCaptcha(phone, captcha string) {
captchaKey := utils.MakeUserCaptchaKey(phone)
err := utils.SetValueToRedis(captchaKey, captcha, 5*time.Minute)
if err != nil {
panic(err)
}
}
// cleanupUser 清理用户数据
func cleanupUser(phone string) {
// 这里应该实现清理用户数据的逻辑
// 由于涉及到数据库操作,这里只是占位符
// 实际实现时需要根据具体的数据库操作来清理
}
================================================
FILE: tests/init_test.go
================================================
package tests
import (
"DiTing-Go/global"
"log"
"os"
"testing"
)
// TestMain 在所有测试运行前执行初始化
func TestMain(m *testing.M) {
// 设置测试环境变量
os.Setenv("GIN_MODE", "test")
os.Setenv("TEST_ENV", "test")
// 初始化日志
global.LogInit()
// 初始化Redis
global.RedisInit()
// 初始化数据库
global.DBInit()
// 运行测试
code := m.Run()
// 清理资源
cleanup()
os.Exit(code)
}
// cleanup 清理测试资源
func cleanup() {
// 关闭Redis连接
if global.Rdb != nil {
global.Rdb.Close()
}
// 关闭数据库连接
if global.Query != nil {
// 这里可以添加数据库连接关闭逻辑
}
log.Println("测试资源清理完成")
}
================================================
FILE: tests/integration/user_integration_test.go
================================================
package integration
import (
"DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"DiTing-Go/logic"
"DiTing-Go/service"
"DiTing-Go/utils"
"context"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
// TestUserRegistrationFlow 测试用户注册流程
func TestUserRegistrationFlow(t *testing.T) {
// 测试数据
testPhone := "13800138001"
testUsername := "testuser1"
testPassword := "123456"
testCaptcha := "123456"
// 设置验证码
setupCaptcha(testPhone, testCaptcha)
// 执行注册
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
assert.Contains(t, resp.Message, "注册成功")
// 验证用户是否真的被创建
userExists, err := logic.CheckPhoneInDB(context.Background(), testPhone)
assert.NoError(t, err)
assert.True(t, userExists)
// 清理测试数据
cleanupUser(testPhone)
}
// TestUserLoginFlow 测试用户登录流程
func TestUserLoginFlow(t *testing.T) {
// 先注册一个用户
testPhone := "13800138002"
testUsername := "testuser2"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
registerResp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
// 测试密码登录
loginReq := req.UserLoginReq{
Phone: testPhone,
Password: testPassword,
LoginType: enum.LoginByPassword,
}
loginResp, err := service.LoginService(loginReq)
assert.NoError(t, err)
assert.True(t, loginResp.Success)
// 验证登录响应数据结构
loginData, ok := loginResp.Data.(map[string]interface{})
assert.True(t, ok)
assert.NotEmpty(t, loginData["token"])
assert.NotZero(t, loginData["uid"])
assert.Equal(t, testUsername, loginData["name"])
// 清理测试数据
cleanupUser(testPhone)
}
// TestUserCancelFlow 测试用户注销流程
func TestUserCancelFlow(t *testing.T) {
// 先注册一个用户
testPhone := "13800138003"
testUsername := "testuser3"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
registerResp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, registerResp.Success)
// 设置注销验证码
setupCaptcha(testPhone, testCaptcha)
// 执行注销
ctx := &gin.Context{}
ctx.Set("uid", int64(12345))
cancelReq := req.UserCancelReq{
Captcha: testCaptcha,
}
cancelResp, err := service.CancelService(ctx, cancelReq)
assert.NoError(t, err)
assert.True(t, cancelResp.Success)
assert.Contains(t, cancelResp.Message, "注销成功")
// 验证用户是否真的被删除
userExists, err := logic.CheckPhoneInDB(context.Background(), testPhone)
assert.NoError(t, err)
assert.False(t, userExists)
}
// TestDuplicateRegistration 测试重复注册
func TestDuplicateRegistration(t *testing.T) {
testPhone := "13800138004"
testUsername := "testuser4"
testPassword := "123456"
testCaptcha := "123456"
// 第一次注册
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp1, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp1.Success)
// 第二次注册相同手机号
setupCaptcha(testPhone, testCaptcha)
resp2, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "手机号已存在")
// 清理测试数据
cleanupUser(testPhone)
}
// TestLoginWithWrongCredentials 测试错误凭据登录
func TestLoginWithWrongCredentials(t *testing.T) {
// 先注册一个用户
testPhone := "13800138005"
testUsername := "testuser5"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 测试错误密码
wrongPasswordReq := req.UserLoginReq{
Phone: testPhone,
Password: "wrongpassword",
LoginType: enum.LoginByPassword,
}
resp2, err := service.LoginService(wrongPasswordReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "用户名或密码错误")
// 测试错误验证码
wrongCaptchaReq := req.UserLoginReq{
Phone: testPhone,
Captcha: "999999",
LoginType: enum.LoginByPhoneCaptcha,
}
resp3, err := service.LoginService(wrongCaptchaReq)
assert.NoError(t, err)
assert.False(t, resp3.Success)
assert.Contains(t, resp3.Message, "验证码错误")
// 清理测试数据
cleanupUser(testPhone)
}
// TestCancelWithWrongCaptcha 测试错误验证码注销
func TestCancelWithWrongCaptcha(t *testing.T) {
// 先注册一个用户
testPhone := "13800138006"
testUsername := "testuser6"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 测试错误验证码注销
ctx := &gin.Context{}
ctx.Set("uid", int64(12345))
wrongCaptchaReq := req.UserCancelReq{
Captcha: "999999",
}
resp2, err := service.CancelService(ctx, wrongCaptchaReq)
assert.NoError(t, err)
assert.False(t, resp2.Success)
assert.Contains(t, resp2.Message, "验证码错误")
// 清理测试数据
cleanupUser(testPhone)
}
// TestUserRegistrationWithValidData 测试有效数据注册
func TestUserRegistrationWithValidData(t *testing.T) {
testCases := []struct {
name string
username string
password string
phone string
captcha string
}{
{
name: "标准注册",
username: "testuser7",
password: "123456",
phone: "13800138007",
captcha: "123456",
},
{
name: "长用户名",
username: "verylongusername123",
password: "123456",
phone: "13800138008",
captcha: "123456",
},
{
name: "复杂密码",
username: "testuser9",
password: "P@ssw0rd123",
phone: "13800138009",
captcha: "123456",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
registerReq := req.UserRegisterReq{
Username: tc.username,
Password: tc.password,
Phone: tc.phone,
Captcha: tc.captcha,
}
setupCaptcha(tc.phone, tc.captcha)
resp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
// 清理测试数据
cleanupUser(tc.phone)
})
}
}
// setupCaptcha 设置验证码
func setupCaptcha(phone, captcha string) {
captchaKey := utils.MakeUserCaptchaKey(phone)
err := utils.SetValueToRedis(captchaKey, captcha, 5*time.Minute)
if err != nil {
panic(err)
}
}
// cleanupUser 清理用户数据
func cleanupUser(phone string) {
// 这里应该实现清理用户数据的逻辑
// 由于涉及到数据库操作,这里只是占位符
// 实际实现时需要根据具体的数据库操作来清理
}
================================================
FILE: tests/performance/user_performance_test.go
================================================
package performance
import (
"DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"DiTing-Go/service"
"DiTing-Go/utils"
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// BenchmarkUserRegistration 用户注册性能基准测试
func BenchmarkUserRegistration(b *testing.B) {
// 重置计时器
b.ResetTimer()
for i := 0; i < b.N; i++ {
phone := fmt.Sprintf("13800139%03d", i%1000)
username := fmt.Sprintf("benchuser%d", i)
password := "123456"
captcha := "123456"
// 设置验证码
setupCaptcha(phone, captcha)
// 执行注册
registerReq := req.UserRegisterReq{
Username: username,
Password: password,
Phone: phone,
Captcha: captcha,
}
resp, err := service.RegisterService(registerReq)
assert.NoError(b, err)
assert.True(b, resp.Success)
// 清理测试数据
cleanupUser(phone)
}
}
// BenchmarkUserLogin 用户登录性能基准测试
func BenchmarkUserLogin(b *testing.B) {
// 先注册一个用户用于测试
testPhone := "13800139000"
testUsername := "benchuser"
testPassword := "123456"
testCaptcha := "123456"
setupCaptcha(testPhone, testCaptcha)
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
resp, err := service.RegisterService(registerReq)
assert.NoError(b, err)
assert.True(b, resp.Success)
// 重置计时器
b.ResetTimer()
for i := 0; i < b.N; i++ {
loginReq := req.UserLoginReq{
Phone: testPhone,
Password: testPassword,
LoginType: enum.LoginByPassword,
}
loginResp, err := service.LoginService(loginReq)
assert.NoError(b, err)
assert.True(b, loginResp.Success)
}
// 清理测试数据
cleanupUser(testPhone)
}
// TestConcurrentUserRegistration 并发用户注册测试
func TestConcurrentUserRegistration(t *testing.T) {
const numUsers = 10
const numGoroutines = 5
var wg sync.WaitGroup
errors := make(chan error, numUsers*numGoroutines)
startTime := time.Now()
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func(routineID int) {
defer wg.Done()
for j := 0; j < numUsers; j++ {
userID := routineID*numUsers + j
phone := fmt.Sprintf("13800140%03d", userID)
username := fmt.Sprintf("concurrentuser%d", userID)
password := "123456"
captcha := "123456"
// 设置验证码
setupCaptcha(phone, captcha)
// 执行注册
registerReq := req.UserRegisterReq{
Username: username,
Password: password,
Phone: phone,
Captcha: captcha,
}
resp, err := service.RegisterService(registerReq)
if err != nil {
errors <- err
return
}
if !resp.Success {
errors <- fmt.Errorf("注册失败: %s", resp.Message)
return
}
// 清理测试数据
cleanupUser(phone)
}
}(i)
}
wg.Wait()
close(errors)
duration := time.Since(startTime)
t.Logf("并发注册 %d 个用户,耗时: %v", numUsers*numGoroutines, duration)
// 检查是否有错误
for err := range errors {
t.Errorf("并发注册错误: %v", err)
}
}
// TestConcurrentUserLogin 并发用户登录测试
func TestConcurrentUserLogin(t *testing.T) {
const numUsers = 10
const numGoroutines = 5
// 先注册一些用户
users := make([]string, numUsers*numGoroutines)
for i := 0; i < numUsers*numGoroutines; i++ {
phone := fmt.Sprintf("13800141%03d", i)
username := fmt.Sprintf("loginuser%d", i)
password := "123456"
captcha := "123456"
setupCaptcha(phone, captcha)
registerReq := req.UserRegisterReq{
Username: username,
Password: password,
Phone: phone,
Captcha: captcha,
}
resp, err := service.RegisterService(registerReq)
assert.NoError(t, err)
assert.True(t, resp.Success)
users[i] = phone
}
var wg sync.WaitGroup
errors := make(chan error, numUsers*numGoroutines)
startTime := time.Now()
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func(routineID int) {
defer wg.Done()
for j := 0; j < numUsers; j++ {
userID := routineID*numUsers + j
phone := users[userID]
password := "123456"
loginReq := req.UserLoginReq{
Phone: phone,
Password: password,
LoginType: enum.LoginByPassword,
}
resp, err := service.LoginService(loginReq)
if err != nil {
errors <- err
return
}
if !resp.Success {
errors <- fmt.Errorf("登录失败: %s", resp.Message)
return
}
}
}(i)
}
wg.Wait()
close(errors)
duration := time.Since(startTime)
t.Logf("并发登录 %d 个用户,耗时: %v", numUsers*numGoroutines, duration)
// 检查是否有错误
for err := range errors {
t.Errorf("并发登录错误: %v", err)
}
// 清理测试数据
for _, phone := range users {
cleanupUser(phone)
}
}
// TestLoadTest 负载测试
func TestLoadTest(t *testing.T) {
const numUsers = 100
const duration = 30 * time.Second
startTime := time.Now()
successCount := 0
errorCount := 0
var mu sync.Mutex
// 创建用户注册任务
for i := 0; i < numUsers; i++ {
go func(userID int) {
phone := fmt.Sprintf("13800142%03d", userID)
username := fmt.Sprintf("loaduser%d", userID)
password := "123456"
captcha := "123456"
setupCaptcha(phone, captcha)
registerReq := req.UserRegisterReq{
Username: username,
Password: password,
Phone: phone,
Captcha: captcha,
}
resp, err := service.RegisterService(registerReq)
mu.Lock()
if err != nil || !resp.Success {
errorCount++
} else {
successCount++
}
mu.Unlock()
// 清理测试数据
cleanupUser(phone)
}(i)
}
// 等待指定时间
time.Sleep(duration)
totalTime := time.Since(startTime)
t.Logf("负载测试结果:")
t.Logf(" 总时间: %v", totalTime)
t.Logf(" 成功请求: %d", successCount)
t.Logf(" 失败请求: %d", errorCount)
t.Logf(" 成功率: %.2f%%", float64(successCount)/float64(successCount+errorCount)*100)
}
// setupCaptcha 设置验证码
func setupCaptcha(phone, captcha string) {
captchaKey := utils.MakeUserCaptchaKey(phone)
err := utils.SetValueToRedis(captchaKey, captcha, 5*time.Minute)
if err != nil {
panic(err)
}
}
// cleanupUser 清理用户数据
func cleanupUser(phone string) {
// 这里应该实现清理用户数据的逻辑
// 由于涉及到数据库操作,这里只是占位符
// 实际实现时需要根据具体的数据库操作来清理
}
================================================
FILE: tests/scripts/run_e2e_test.sh
================================================
#!/bin/bash
# DiTing-Go E2E测试运行脚本
echo "=== DiTing-Go E2E测试运行脚本 ==="
echo ""
# 检查Go环境
if ! command -v go &> /dev/null; then
echo "错误: 未找到Go环境,请先安装Go"
exit 1
fi
# 获取项目根目录
PROJECT_ROOT=$(pwd)
echo "项目根目录: $PROJECT_ROOT"
# 设置环境变量
export PROJECT_ROOT="$PROJECT_ROOT"
export GIN_MODE=test
export TEST_ENV=test
echo "环境变量设置:"
echo " PROJECT_ROOT=$PROJECT_ROOT"
echo " GIN_MODE=$GIN_MODE"
echo " TEST_ENV=$TEST_ENV"
# 检查配置文件
if [ -f "$PROJECT_ROOT/conf/config.yml" ]; then
echo "✅ 配置文件存在: $PROJECT_ROOT/conf/config.yml"
else
echo "❌ 配置文件不存在: $PROJECT_ROOT/conf/config.yml"
exit 1
fi
# 检查依赖
echo "检查依赖..."
go mod tidy
echo ""
echo "=== 运行E2E测试 ==="
# 确保在项目根目录运行测试
cd "$PROJECT_ROOT"
# 运行特定的E2E测试
echo "运行用户完整工作流E2E测试..."
go test -v -run TestUserCompleteWorkflow ./tests/e2e/
echo ""
echo "运行多用户并发E2E测试..."
go test -v -run TestMultipleUserWorkflow ./tests/e2e/
echo ""
echo "运行用户错误场景E2E测试..."
go test -v -run TestUserErrorScenarios ./tests/e2e/
echo ""
echo "=== E2E测试完成 ==="
================================================
FILE: tests/scripts/run_tests.bat
================================================
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
echo === DiTing-Go 用户服务测试 ===
echo.
REM 检查Go环境
where go >nul 2>nul
if %errorlevel% neq 0 (
echo 错误: 未找到Go环境,请先安装Go
pause
exit /b 1
)
REM 检查依赖
echo 检查依赖...
go mod tidy
REM 设置测试环境变量
set GIN_MODE=test
:menu
echo.
echo 请选择要运行的测试:
echo 1. 用户完整生命周期测试
echo 2. 用户注册流程测试
echo 3. 用户登录流程测试
echo 4. 用户注销流程测试
echo 5. 注册参数验证测试
echo 6. 登录参数验证测试
echo 7. 重复注册测试
echo 8. 错误凭据登录测试
echo 9. 错误验证码注销测试
echo 10. 有效数据注册测试
echo 11. 运行所有测试
echo 0. 退出
echo.
set /p choice=请输入选项 (0-11):
if "%choice%"=="1" goto test_lifecycle
if "%choice%"=="2" goto test_register
if "%choice%"=="3" goto test_login
if "%choice%"=="4" goto test_cancel
if "%choice%"=="5" goto test_register_validation
if "%choice%"=="6" goto test_login_validation
if "%choice%"=="7" goto test_duplicate_register
if "%choice%"=="8" goto test_wrong_credentials
if "%choice%"=="9" goto test_wrong_captcha
if "%choice%"=="10" goto test_valid_data
if "%choice%"=="11" goto test_all
if "%choice%"=="0" goto exit
echo 无效选项,请重新选择
goto menu
:test_lifecycle
echo.
echo === 运行测试: 用户完整生命周期测试 ===
echo 命令: go test -v -run TestUserLifecycleFlow
echo.
go test -v -run TestUserLifecycleFlow ./service/
if %errorlevel% equ 0 (
echo ✅ 用户完整生命周期测试通过
) else (
echo ❌ 用户完整生命周期测试失败
)
goto continue
:test_register
echo.
echo === 运行测试: 用户注册流程测试 ===
echo 命令: go test -v -run TestUserRegistrationFlow
echo.
go test -v -run TestUserRegistrationFlow ./service/
if %errorlevel% equ 0 (
echo ✅ 用户注册流程测试通过
) else (
echo ❌ 用户注册流程测试失败
)
goto continue
:test_login
echo.
echo === 运行测试: 用户登录流程测试 ===
echo 命令: go test -v -run TestUserLoginFlow
echo.
go test -v -run TestUserLoginFlow ./service/
if %errorlevel% equ 0 (
echo ✅ 用户登录流程测试通过
) else (
echo ❌ 用户登录流程测试失败
)
goto continue
:test_cancel
echo.
echo === 运行测试: 用户注销流程测试 ===
echo 命令: go test -v -run TestUserCancelFlow
echo.
go test -v -run TestUserCancelFlow ./service/
if %errorlevel% equ 0 (
echo ✅ 用户注销流程测试通过
) else (
echo ❌ 用户注销流程测试失败
)
goto continue
:test_register_validation
echo.
echo === 运行测试: 注册参数验证测试 ===
echo 命令: go test -v -run TestRegisterValidation
echo.
go test -v -run TestRegisterValidation ./service/
if %errorlevel% equ 0 (
echo ✅ 注册参数验证测试通过
) else (
echo ❌ 注册参数验证测试失败
)
goto continue
:test_login_validation
echo.
echo === 运行测试: 登录参数验证测试 ===
echo 命令: go test -v -run TestLoginValidation
echo.
go test -v -run TestLoginValidation ./service/
if %errorlevel% equ 0 (
echo ✅ 登录参数验证测试通过
) else (
echo ❌ 登录参数验证测试失败
)
goto continue
:test_duplicate_register
echo.
echo === 运行测试: 重复注册测试 ===
echo 命令: go test -v -run TestDuplicateRegistration
echo.
go test -v -run TestDuplicateRegistration ./service/
if %errorlevel% equ 0 (
echo ✅ 重复注册测试通过
) else (
echo ❌ 重复注册测试失败
)
goto continue
:test_wrong_credentials
echo.
echo === 运行测试: 错误凭据登录测试 ===
echo 命令: go test -v -run TestLoginWithWrongCredentials
echo.
go test -v -run TestLoginWithWrongCredentials ./service/
if %errorlevel% equ 0 (
echo ✅ 错误凭据登录测试通过
) else (
echo ❌ 错误凭据登录测试失败
)
goto continue
:test_wrong_captcha
echo.
echo === 运行测试: 错误验证码注销测试 ===
echo 命令: go test -v -run TestCancelWithWrongCaptcha
echo.
go test -v -run TestCancelWithWrongCaptcha ./service/
if %errorlevel% equ 0 (
echo ✅ 错误验证码注销测试通过
) else (
echo ❌ 错误验证码注销测试失败
)
goto continue
:test_valid_data
echo.
echo === 运行测试: 有效数据注册测试 ===
echo 命令: go test -v -run TestUserRegistrationWithValidData
echo.
go test -v -run TestUserRegistrationWithValidData ./service/
if %errorlevel% equ 0 (
echo ✅ 有效数据注册测试通过
) else (
echo ❌ 有效数据注册测试失败
)
goto continue
:test_all
echo.
echo === 运行所有测试 ===
set failed_tests=
for %%t in (TestUserLifecycleFlow TestUserRegistrationFlow TestUserLoginFlow TestUserCancelFlow TestRegisterValidation TestLoginValidation TestDuplicateRegistration TestLoginWithWrongCredentials TestCancelWithWrongCaptcha TestUserRegistrationWithValidData) do (
echo.
echo 运行测试: %%t
go test -v -run %%t ./service/
if !errorlevel! neq 0 (
set failed_tests=!failed_tests! %%t
)
)
echo.
echo === 测试结果汇总 ===
if "%failed_tests%"=="" (
echo ✅ 所有测试通过
) else (
echo ❌ 以下测试失败:
for %%t in (%failed_tests%) do (
echo - %%t
)
)
goto continue
:continue
echo.
pause
goto menu
:exit
echo 退出测试
pause
exit /b 0
================================================
FILE: tests/scripts/run_tests.sh
================================================
#!/bin/bash
# DiTing-Go 测试运行脚本
echo "=== DiTing-Go 测试运行脚本 ==="
echo ""
# 检查Go环境
if ! command -v go &> /dev/null; then
echo "错误: 未找到Go环境,请先安装Go"
exit 1
fi
# 检查依赖
echo "检查依赖..."
go mod tidy
# 获取项目根目录并设置环境变量
PROJECT_ROOT=$(pwd)
export PROJECT_ROOT="$PROJECT_ROOT"
export GIN_MODE=test
export TEST_ENV=test
echo "项目根目录: $PROJECT_ROOT"
echo "环境变量设置完成"
# 运行测试的函数
run_test() {
local test_name=$1
local test_pattern=$2
local test_dir=$3
echo ""
echo "=== 运行测试: $test_name ==="
echo "命令: go test -v -run $test_pattern $test_dir"
echo ""
# 确保在项目根目录运行测试
cd "$PROJECT_ROOT"
go test -v -run "$test_pattern" "$test_dir"
if [ $? -eq 0 ]; then
echo "✅ $test_name 测试通过"
else
echo "❌ $test_name 测试失败"
return 1
fi
}
# 主菜单
show_menu() {
echo ""
echo "请选择要运行的测试:"
echo "=== 单元测试 ==="
echo "1. 用户服务单元测试"
echo "2. 用户生命周期单元测试"
echo "3. 注册验证单元测试"
echo "4. 登录验证单元测试"
echo ""
echo "=== 集成测试 ==="
echo "5. 用户注册集成测试"
echo "6. 用户登录集成测试"
echo "7. 用户注销集成测试"
echo "8. 重复注册集成测试"
echo "9. 错误凭据登录集成测试"
echo "10. 错误验证码注销集成测试"
echo "11. 有效数据注册集成测试"
echo ""
echo "=== 端到端测试 ==="
echo "12. 用户完整工作流E2E测试"
echo "13. 多用户并发E2E测试"
echo "14. 用户错误场景E2E测试"
echo ""
echo "=== 性能测试 ==="
echo "15. 用户注册性能测试"
echo "16. 用户登录性能测试"
echo "17. 并发用户注册测试"
echo "18. 并发用户登录测试"
echo "19. 负载测试"
echo ""
echo "=== 批量测试 ==="
echo "20. 运行所有单元测试"
echo "21. 运行所有集成测试"
echo "22. 运行所有E2E测试"
echo "23. 运行所有性能测试"
echo "24. 运行所有测试"
echo "0. 退出"
echo ""
read -p "请输入选项 (0-24): " choice
}
# 运行所有测试
run_all_tests() {
echo ""
echo "=== 运行所有测试 ==="
# 确保在项目根目录运行测试
cd "$PROJECT_ROOT"
test_dirs=(
"./tests/unit/"
"./tests/integration/"
"./tests/e2e/"
"./tests/performance/"
)
failed_tests=()
for dir in "${test_dirs[@]}"; do
echo ""
echo "运行测试目录: $dir"
go test -v "$dir"
if [ $? -ne 0 ]; then
failed_tests+=("$dir")
fi
done
echo ""
echo "=== 测试结果汇总 ==="
if [ ${#failed_tests[@]} -eq 0 ]; then
echo "✅ 所有测试通过"
else
echo "❌ 以下测试失败:"
for test in "${failed_tests[@]}"; do
echo " - $test"
done
fi
}
# 主循环
while true; do
show_menu
case $choice in
1)
run_test "用户服务单元测试" "TestUserLifecycleFlow" "./tests/unit/"
;;
2)
run_test "用户生命周期单元测试" "TestUserLifecycleFlow" "./tests/unit/"
;;
3)
run_test "注册验证单元测试" "TestRegisterValidation" "./tests/unit/"
;;
4)
run_test "登录验证单元测试" "TestLoginValidation" "./tests/unit/"
;;
5)
run_test "用户注册集成测试" "TestUserRegistrationFlow" "./tests/integration/"
;;
6)
run_test "用户登录集成测试" "TestUserLoginFlow" "./tests/integration/"
;;
7)
run_test "用户注销集成测试" "TestUserCancelFlow" "./tests/integration/"
;;
8)
run_test "重复注册集成测试" "TestDuplicateRegistration" "./tests/integration/"
;;
9)
run_test "错误凭据登录集成测试" "TestLoginWithWrongCredentials" "./tests/integration/"
;;
10)
run_test "错误验证码注销集成测试" "TestCancelWithWrongCaptcha" "./tests/integration/"
;;
11)
run_test "有效数据注册集成测试" "TestUserRegistrationWithValidData" "./tests/integration/"
;;
12)
run_test "用户完整工作流E2E测试" "TestUserCompleteWorkflow" "./tests/e2e/"
;;
13)
run_test "多用户并发E2E测试" "TestMultipleUserWorkflow" "./tests/e2e/"
;;
14)
run_test "用户错误场景E2E测试" "TestUserErrorScenarios" "./tests/e2e/"
;;
15)
run_test "用户注册性能测试" "BenchmarkUserRegistration" "./tests/performance/"
;;
16)
run_test "用户登录性能测试" "BenchmarkUserLogin" "./tests/performance/"
;;
17)
run_test "并发用户注册测试" "TestConcurrentUserRegistration" "./tests/performance/"
;;
18)
run_test "并发用户登录测试" "TestConcurrentUserLogin" "./tests/performance/"
;;
19)
run_test "负载测试" "TestLoadTest" "./tests/performance/"
;;
20)
echo ""
echo "=== 运行所有单元测试 ==="
cd "$PROJECT_ROOT"
go test -v ./tests/unit/...
;;
21)
echo ""
echo "=== 运行所有集成测试 ==="
cd "$PROJECT_ROOT"
go test -v ./tests/integration/...
;;
22)
echo ""
echo "=== 运行所有E2E测试 ==="
cd "$PROJECT_ROOT"
go test -v ./tests/e2e/...
;;
23)
echo ""
echo "=== 运行所有性能测试 ==="
cd "$PROJECT_ROOT"
go test -v ./tests/performance/...
;;
24)
run_all_tests
;;
0)
echo "退出测试"
exit 0
;;
*)
echo "无效选项,请重新选择"
;;
esac
echo ""
read -p "按回车键继续..."
done
================================================
FILE: tests/scripts/setup_test_env.sh
================================================
#!/bin/bash
# DiTing-Go 测试环境设置脚本
echo "=== DiTing-Go 测试环境设置 ==="
echo ""
# 检查Go环境
if ! command -v go &> /dev/null; then
echo "错误: 未找到Go环境,请先安装Go"
exit 1
fi
# 检查Docker环境(可选)
if command -v docker &> /dev/null; then
echo "✅ 检测到Docker环境"
else
echo "⚠️ 未检测到Docker环境,将使用本地服务"
fi
# 获取项目根目录
PROJECT_ROOT=$(pwd)
echo "项目根目录: $PROJECT_ROOT"
# 设置测试环境变量
echo "设置测试环境变量..."
export GIN_MODE=test
export TEST_ENV=test
export PROJECT_ROOT="$PROJECT_ROOT"
# 检查配置文件是否存在
if [ -f "$PROJECT_ROOT/conf/config.yml" ]; then
echo "✅ 配置文件存在: $PROJECT_ROOT/conf/config.yml"
else
echo "❌ 配置文件不存在: $PROJECT_ROOT/conf/config.yml"
exit 1
fi
# 检查数据库连接
echo "检查数据库连接..."
# 这里可以添加数据库连接检查逻辑
# 检查Redis连接
echo "检查Redis连接..."
# 这里可以添加Redis连接检查逻辑
# 准备测试数据
echo "准备测试数据..."
# 这里可以添加测试数据准备逻辑
# 清理旧的测试数据
echo "清理旧的测试数据..."
# 这里可以添加清理逻辑
echo ""
echo "✅ 测试环境设置完成"
echo ""
echo "环境变量设置:"
echo " GIN_MODE=$GIN_MODE"
echo " TEST_ENV=$TEST_ENV"
echo " PROJECT_ROOT=$PROJECT_ROOT"
echo ""
echo "可以运行以下命令开始测试:"
echo " ./tests/scripts/run_tests.sh"
echo " go test -v ./tests/..."
echo ""
================================================
FILE: tests/unit/user_service_test.go
================================================
package unit
import (
"DiTing-Go/domain/enum"
"DiTing-Go/domain/vo/req"
"DiTing-Go/global"
"DiTing-Go/service"
"DiTing-Go/utils"
"DiTing-Go/utils/setting"
"testing"
"time"
"DiTing-Go/logic"
"context"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
// 初始化函数,在包加载时执行
func init() {
// 设置测试环境变量
gin.SetMode(gin.TestMode)
// 初始化配置
setting.ConfigInit()
// 初始化简单的测试日志
global.Logger = logrus.New()
global.Logger.SetOutput(gin.DefaultWriter)
global.Logger.SetLevel(logrus.InfoLevel)
// 初始化Redis
global.RedisInit()
// 初始化数据库
global.DBInit()
}
// TestRegisterValidation 测试注册参数验证
func TestRegisterValidation(t *testing.T) {
testCases := []struct {
name string
req req.UserRegisterReq
expected string
shouldSuccess bool
}{
{
name: "用户名为空",
req: req.UserRegisterReq{
Username: "",
Password: "123456",
Phone: "13800138001",
Captcha: "123456",
},
expected: "用户名、密码和手机号不能为空",
shouldSuccess: false,
},
{
name: "密码长度不足",
req: req.UserRegisterReq{
Username: "testuser",
Password: "123",
Phone: "13800138001",
Captcha: "123456",
},
expected: "密码长度不能少于6位",
shouldSuccess: false,
},
{
name: "正确注册用例",
req: req.UserRegisterReq{
Username: "validuser",
Password: "123456",
Phone: "13800138002",
Captcha: "123456",
},
expected: "",
shouldSuccess: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// 为正确用例设置验证码
if tc.shouldSuccess {
setupCaptcha(tc.req.Phone, tc.req.Captcha)
}
resp, err := service.RegisterService(tc.req)
if tc.shouldSuccess {
// 正确用例应该成功
assert.NoError(t, err)
assert.True(t, resp.Success, "注册应该成功")
assert.Contains(t, resp.Message, "注册成功")
} else {
// 错误用例应该失败
assert.Contains(t, resp.Message, tc.expected)
}
})
}
}
// TestLoginValidation 测试登录参数验证
func TestLoginValidation(t *testing.T) {
testCases := []struct {
name string
req req.UserLoginReq
expected string
shouldSuccess bool
}{
{
name: "密码登录-手机号为空",
req: req.UserLoginReq{
Phone: "",
Password: "123456",
LoginType: enum.LoginByPassword,
},
expected: "用户名和密码不能为空",
shouldSuccess: false,
},
{
name: "验证码登录-手机号为空",
req: req.UserLoginReq{
Phone: "",
Captcha: "123456",
LoginType: enum.LoginByPhoneCaptcha,
},
expected: "手机号和验证码不能为空",
shouldSuccess: false,
},
{
name: "正确密码登录用例",
req: req.UserLoginReq{
Phone: "13800138002",
Password: "123456",
LoginType: enum.LoginByPassword,
},
expected: "",
shouldSuccess: true,
},
{
name: "正确验证码登录用例",
req: req.UserLoginReq{
Phone: "13800138002",
Captcha: "123456",
LoginType: enum.LoginByPhoneCaptcha,
},
expected: "",
shouldSuccess: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// 为正确用例设置验证码
if tc.shouldSuccess && tc.req.LoginType == enum.LoginByPhoneCaptcha {
setupCaptcha(tc.req.Phone, tc.req.Captcha)
}
resp, err := service.LoginService(tc.req)
if tc.shouldSuccess {
// 正确用例应该成功
assert.NoError(t, err)
assert.True(t, resp.Success, "登录应该成功")
// 验证返回的数据结构
loginData, ok := resp.Data.(map[string]interface{})
assert.True(t, ok, "返回数据应该是map类型")
assert.NotEmpty(t, loginData["token"], "应该返回token")
assert.NotZero(t, loginData["uid"], "应该返回用户ID")
} else {
// 错误用例应该失败
assert.Contains(t, resp.Message, tc.expected)
}
})
}
}
// TestCheckPassword 测试密码校验功能
func TestCheckPassword(t *testing.T) {
ctx := context.Background()
// 测试边界情况
t.Run("空手机号", func(t *testing.T) {
result := logic.CheckPassword(ctx, "", "123456")
assert.False(t, result, "空手机号应该校验失败")
})
t.Run("空密码", func(t *testing.T) {
result := logic.CheckPassword(ctx, "13800138003", "")
assert.False(t, result, "空密码应该校验失败")
})
t.Run("短密码", func(t *testing.T) {
result := logic.CheckPassword(ctx, "13800138003", "123")
assert.False(t, result, "短密码应该校验失败")
})
t.Run("不存在的手机号", func(t *testing.T) {
result := logic.CheckPassword(ctx, "99999999999", "123456")
assert.False(t, result, "不存在的手机号应该校验失败")
})
// 测试正常情况(需要先注册用户)
t.Run("正常密码校验流程", func(t *testing.T) {
// 先注册一个测试用户
testPhone := "13800138004"
testUsername := "testuser"
testPassword := "123456"
testCaptcha := "123456"
// 设置验证码
setupCaptcha(testPhone, testCaptcha)
// 注册用户
registerReq := req.UserRegisterReq{
Username: testUsername,
Password: testPassword,
Phone: testPhone,
Captcha: testCaptcha,
}
registerResp, err := service.RegisterService(registerReq)
if err == nil && registerResp.Success {
// 注册成功,测试密码校验
t.Run("正确密码", func(t *testing.T) {
result := logic.CheckPassword(ctx, testPhone, testPassword)
assert.True(t, result, "正确密码应该通过校验")
})
t.Run("错误密码", func(t *testing.T) {
result := logic.CheckPassword(ctx, testPhone, "wrongpassword")
assert.False(t, result, "错误密码应该校验失败")
})
// 清理测试数据
cleanupUser(testPhone)
} else {
t.Skip("用户注册失败,跳过密码校验测试")
}
})
}
// setupCaptcha 设置验证码
func setupCaptcha(phone, captcha string) {
captchaKey := utils.MakeUserCaptchaKey(phone)
err := utils.SetValueToRedis(captchaKey, captcha, 5*time.Minute)
if err != nil {
panic(err)
}
}
// cleanupUser 清理用户数据
func cleanupUser(phone string) {
// 这里应该实现清理用户数据的逻辑
// 由于涉及到数据库操作,这里只是占位符
// 实际实现时需要根据具体的数据库操作来清理
global.Logger.Infof("cleanupUser: cleaning up user data for phone: %s", phone)
}
================================================
FILE: utils/jsonUtils/json.go
================================================
package jsonUtils
//import (
// "DiTing-Go/global"
// "context"
// "github.com/apache/rocketmq-client-go/v2/primitive"
// "github.com/goccy/go-json"
// "github.com/pkg/errors"
//)
//
//// UnmarshalMsg 解析消息队列msg
//func UnmarshalMsg(item any, msg *primitive.MessageExt) error {
// byteStr := msg.Message.Body
// err := json.Unmarshal(byteStr, item)
// if err != nil {
// global.Logger.Errorf("jsonUtils unmarshal error: %s", err.Error())
// return errors.New("Business Error")
// }
// return nil
//}
//
//// Marshal 编码
//func Marshal(item any) ([]byte, error) {
// byteStr, err := json.Marshal(item)
// if err != nil {
// global.Logger.Errorf("json序列化失败 %v", err)
// return nil, err
// }
// return byteStr, nil
//}
//
//// SendMsgSync 发送消息
//func SendMsgSync(topic string, item any) error {
// ctx := context.Background()
// byteStr, err := Marshal(item)
// if err != nil {
// return err
// }
// msg := &primitive.Message{
// Topic: topic,
// Body: byteStr,
// }
// _, err = global.RocketProducer.SendSync(ctx, msg)
// return err
//}
================================================
FILE: utils/jwt/jwt.go
================================================
package jwt
import (
"DiTing-Go/global"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"github.com/spf13/viper"
)
var jwtSecret = []byte(viper.GetString("jwt.secret"))
type JwtClaims struct {
Uid int64 `json:"uid"`
jwt.RegisteredClaims
}
// GenerateToken 生成token
func GenerateToken(uid int64) (string, error) {
registeredClaims := jwt.RegisteredClaims{
// A usual scenario is to set the expiration time relative to the current time
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
}
claims := JwtClaims{
uid,
registeredClaims,
}
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := tokenClaims.SignedString(jwtSecret)
if err != nil {
global.Logger.Errorf("generate token failed: %v", err)
}
return token, err
}
// ParseToken 解析token
func ParseToken(tokenString string) (*JwtClaims, error) {
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (any, error) {
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("token无法解析")
}
================================================
FILE: utils/jwt/jwt_test.go
================================================
package jwt
import (
"fmt"
"testing"
"time"
)
func TestGenerateToken(t *testing.T) {
tests := []struct {
name string
uid int64
wantErr bool
}{
{
name: "正常生成token",
uid: 12345,
wantErr: false,
},
{
name: "用户ID为0",
uid: 0,
wantErr: false,
},
{
name: "用户ID为负数",
uid: -1,
wantErr: false,
},
{
name: "大用户ID",
uid: 999999999,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
token, err := GenerateToken(tt.uid)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateToken() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && token == "" {
t.Error("GenerateToken() returned empty token")
}
})
}
}
func TestParseToken(t *testing.T) {
// 首先生成一个有效的token用于测试
validUID := int64(12345)
validToken, err := GenerateToken(validUID)
if err != nil {
t.Fatalf("Failed to generate valid token for testing: %v", err)
}
tests := []struct {
name string
token string
wantUID int64
wantErr bool
}{
{
name: "解析有效token",
token: validToken,
wantUID: validUID,
wantErr: false,
},
{
name: "解析空token",
token: "",
wantUID: 0,
wantErr: true,
},
{
name: "解析无效token",
token: "invalid.token.here",
wantUID: 0,
wantErr: true,
},
{
name: "解析格式错误的token",
token: "not.a.valid.jwt.token",
wantUID: 0,
wantErr: true,
},
{
name: "解析过期token",
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEyMzQ1LCJleHAiOjE2MzQ1Njc4OTB9.invalid_signature",
wantUID: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
claims, err := ParseToken(tt.token)
if (err != nil) != tt.wantErr {
t.Errorf("ParseToken() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
if claims == nil {
t.Error("ParseToken() returned nil claims for valid token")
return
}
if claims.Uid != tt.wantUID {
t.Errorf("ParseToken() UID = %v, want %v", claims.Uid, tt.wantUID)
}
}
})
}
}
func TestGenerateAndParseToken(t *testing.T) {
// 测试生成token后立即解析是否能得到相同的UID
testUIDs := []int64{1, 100, 1000, 999999}
for _, uid := range testUIDs {
t.Run(fmt.Sprintf("UID_%d", uid), func(t *testing.T) {
// 生成token
token, err := GenerateToken(uid)
if err != nil {
t.Errorf("GenerateToken() failed: %v", err)
return
}
// 解析token
claims, err := ParseToken(token)
if err != nil {
t.Errorf("ParseToken() failed: %v", err)
return
}
// 验证UID是否一致
if claims.Uid != uid {
t.Errorf("UID mismatch: got %v, want %v", claims.Uid, uid)
}
// 验证token的过期时间
if claims.ExpiresAt == nil {
t.Error("Token expiration time is nil")
} else if claims.ExpiresAt.Time.Before(time.Now()) {
t.Error("Token has already expired")
}
})
}
}
func TestTokenExpiration(t *testing.T) {
uid := int64(12345)
// 生成token
token, err := GenerateToken(uid)
if err != nil {
t.Fatalf("Failed to generate token: %v", err)
}
// 解析token
claims, err := ParseToken(token)
if err != nil {
t.Fatalf("Failed to parse token: %v", err)
}
// 验证token的过期时间是否在合理范围内(24小时)
expectedExpiry := time.Now().Add(24 * time.Hour)
actualExpiry := claims.ExpiresAt.Time
// 允许1分钟的误差
tolerance := time.Minute
if actualExpiry.After(expectedExpiry.Add(tolerance)) || actualExpiry.Before(expectedExpiry.Add(-tolerance)) {
t.Errorf("Token expiration time is not within expected range. Expected around %v, got %v", expectedExpiry, actualExpiry)
}
}
================================================
FILE: utils/middleware/cors.go
================================================
package middleware
import (
"github.com/gin-gonic/gin"
"net/http"
)
// Cors 跨域中间件
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin")
if origin != "" {
//接收客户端发送的origin (重要!)
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
//服务器支持的所有跨域请求的方法
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
//允许跨域设置可以返回其他子段,可以自定义字段
c.Header("Access-Control-Allow-Headers", "Authorization, content-type, Content-Length, X-CSRF-Token, Token,session,Access-Control-Allow-Headers,account")
// 允许浏览器(客户端)可以解析的头部 (重要)
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
//设置缓存时间
c.Header("Access-Control-Max-Age", "172800")
//允许客户端传递校验信息比如 cookie (重要)
c.Header("Access-Control-Allow-Credentials", "true")
c.Set("Content-Type", "application/json")
}
//允许类型校验
if method == "OPTIONS" {
c.JSON(http.StatusOK, "ok!")
}
c.Next()
}
}
================================================
FILE: utils/middleware/jwt.go
================================================
package middleware
import (
"DiTing-Go/pkg/domain/vo/resp"
"DiTing-Go/utils/jwt"
"github.com/gin-gonic/gin"
"strings"
)
// JWT jwt中间件
func JWT() gin.HandlerFunc {
return func(c *gin.Context) {
// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
// 这里假设Token放在Header的Authorization中,并使用Bearer开头
// 这里的具体实现方式要依据你的实际业务情况决定
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
resp.ErrorResponse(c, "无权限访问")
c.Abort()
return
}
// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
resp.ErrorResponse(c, "无权限访问")
c.Abort()
return
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
token, err := jwt.ParseToken(parts[1])
if err != nil {
resp.ErrorResponse(c, "无权限访问")
c.Abort()
return
}
//把解析出来的token存储到请求的上下文c上,方便后续的处理函数获取
c.Set("uid", token.Uid)
c.Next()
}
}
================================================
FILE: utils/middleware/log.go
================================================
package middleware
import (
"DiTing-Go/global"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"time"
)
// LoggerToFile 日志记录到文件
func LoggerToFile() gin.HandlerFunc {
return func(c *gin.Context) {
// 开始时间
startTime := time.Now()
// 处理请求
c.Next()
// 结束时间
endTime := time.Now()
// 执行时间
latencyTime := endTime.Sub(startTime)
// 请求方式
reqMethod := c.Request.Method
// 请求路由
reqUri := c.Request.RequestURI
// 状态码
statusCode := c.Writer.Status()
// 请求IP
clientIP := c.ClientIP()
// 日志格式
global.Logger.WithFields(logrus.Fields{
"statusCode": statusCode,
"latencyTime": latencyTime,
"clientIP": clientIP,
"reqMethod": reqMethod,
"reqUri": reqUri,
}).Infof("GIN")
}
}
================================================
FILE: utils/mysqlUtils.go
================================================
package utils
import (
"DiTing-Go/dal/model"
"DiTing-Go/global"
"context"
"errors"
"fmt"
"gorm.io/gorm"
"strconv"
)
// QueryUserByPhone 根据手机号查询用户
func QueryUserByPhone(ctx context.Context, phone string) (*model.User, error) {
user := global.Query.User
userQ := user.WithContext(ctx)
rst, err := userQ.Where(user.Phone.Eq(phone)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
global.Logger.Errorf("user not found with phone: %s", phone)
return nil, gorm.ErrRecordNotFound
}
return nil, fmt.Errorf("query user by phone error: %w", err)
}
return rst, nil
}
// QueryUserByID 根据ID查询用户
func QueryUserByID(ctx context.Context, userId string) (*model.User, error) {
userIdNum, err := strconv.ParseInt(userId, 10, 64)
user := global.Query.User
userQ := user.WithContext(ctx)
rst, err := userQ.Where(user.ID.Eq(userIdNum)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
global.Logger.Errorf("user not found with userId: %s", userId)
return nil, gorm.ErrRecordNotFound
}
return nil, fmt.Errorf("query user by userId error: %v", err)
}
return rst, nil
}
================================================
FILE: utils/passwordUtils.go
================================================
package utils
import (
"crypto/md5"
"encoding/hex"
)
// EncryptPassword 对密码进行md5加密
func EncryptPassword(password string) string {
hash := md5.New()
hash.Write([]byte(password))
password = hex.EncodeToString(hash.Sum(nil))
return password
}
================================================
FILE: utils/redisCache/remove_cache.go
================================================
package redisCache
import (
"DiTing-Go/dal/model"
"DiTing-Go/domain/enum"
"DiTing-Go/pkg/utils"
"fmt"
)
// RemoveRoomCache 移除房间缓存
func RemoveRoomCache(room model.Room) {
utils.RemoveData(fmt.Sprintf(enum.RoomCacheByID, room.ID))
}
// RemoveRoomFriend 移除房间好友缓存
func RemoveRoomFriend(roomFriend model.RoomFriend) {
utils.RemoveData(fmt.Sprintf(enum.RoomFriendCacheByRoomID, roomFriend.RoomID))
utils.RemoveData(fmt.Sprintf(enum.RoomFriendCacheByUidAndFriendUid, roomFriend.Uid1, roomFriend.Uid2))
}
// RemoveUserCache 移除用户缓存
func RemoveUserCache(user model.User) {
utils.RemoveData(fmt.Sprintf(enum.UserCacheByID, user.ID))
utils.RemoveData(fmt.Sprintf(enum.UserCacheByName, user.Name))
}
// RemoveUserFriend 移除用户好友缓存
func RemoveUserFriend(uid, friendUid int64) {
utils.RemoveData(fmt.Sprintf(enum.UserFriendCacheByUidAndFriendUid, uid, friendUid))
utils.RemoveData(fmt.Sprintf(enum.UserFriendCacheByUidAndFriendUid, friendUid, uid))
}
// RemoveUserApply 移除用户好友申请缓存
func RemoveUserApply(uid, friendUid int64) {
utils.RemoveData(fmt.Sprintf(enum.UserApplyCacheByUidAndFriendUid, uid, friendUid))
utils.RemoveData(fmt.Sprintf(enum.UserApplyCacheByUidAndFriendUid, friendUid, uid))
}
// RemoveContact 移除会话缓存
func RemoveContact(contact model.Contact) {
utils.RemoveData(fmt.Sprintf(enum.ContactCacheById, contact.ID))
}
================================================
FILE: utils/redisUtils.go
================================================
package utils
import (
domainEnum "DiTing-Go/domain/enum"
"DiTing-Go/global"
"fmt"
"time"
"github.com/goccy/go-json"
"github.com/pkg/errors"
)
// MakeUserPhoneKey 构造用户手机号
func MakeUserPhoneKey(phone string) string {
return fmt.Sprintf(domainEnum.PhoneUidMap, phone)
}
// MakeUserCaptchaKey 构造验证码key
func MakeUserCaptchaKey(phone string) string {
return fmt.Sprintf(domainEnum.UserCaptcha, phone)
}
// SetValueToRedis 设置字符串
func SetValueToRedis(key string, value string, expireTime time.Duration) error {
valueByte, err := json.Marshal(value)
if err != nil {
global.Logger.Errorf("json marshal error: %v", err)
return errors.New("json marshal error")
}
if err = global.Rdb.Set(key, valueByte, expireTime).Err(); err != nil {
global.Logger.Errorf("key:%s, value:%s, redis set error: %v", key, valueByte, err)
return errors.New("redis set error")
}
return nil
}
// GetValueFromRedis 获取字符串
func GetValueFromRedis(key string) (value []byte, err error) {
valueByte, err := global.Rdb.Get(key).Bytes()
if err != nil {
return nil, err
}
return valueByte, nil
}
// DeleteValueFromRedis 删除字符串
func DeleteValueFromRedis(key string) error {
if err := global.Rdb.Del(key).Err(); err != nil {
global.Logger.Errorf("key:%s, redis delete error: %v", key, err)
return errors.New("redis delete error")
}
return nil
}
//// GetData 获取数据
//func GetData(cacheKey string, value any, dbQueryFunc func() (interface{}, error)) error {
// // 1. 从缓存中获取数据
// err := GetString(cacheKey, value)
// // 查询到数据
// if err == nil {
// return nil
// } else if !errors.Is(err, redis.Nil) {
// return err
// }
// err = QueryAndSet(cacheKey, value, dbQueryFunc)
// if err != nil {
// return err
// }
// return nil
//}
//// QueryAndSet 查询数据库并设置缓存
//func QueryAndSet(cacheKey string, value any, dbQueryFunc func() (interface{}, error)) error {
// // 2. 从数据库中获取数据
// result, err := dbQueryFunc()
// if err != nil {
// return err
// }
// err = copier.Copy(value, result)
// if err != nil {
// global.Logger.Errorf("拷贝数据失败: %v", err)
// return err
// }
// // 3. 将查询结果写回缓存
// if err = SetString(cacheKey, result); err != nil {
// global.Logger.Errorf("写入redis失败: %v", err)
// return err
// }
// return err
//}
//
//func RemoveData(key string) {
// global.Rdb.Del(key)
//}
================================================
FILE: utils/setting/setting.go
================================================
package setting
import (
"log"
"os"
"path/filepath"
"github.com/spf13/viper"
)
func ConfigInit() {
// 设置配置文件的名字
viper.SetConfigName("config")
// 设置配置文件的类型
viper.SetConfigType("yaml")
// 获取当前工作目录
currentDir, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to get current directory: %v", err)
}
// 尝试多个可能的配置文件路径
configPaths := []string{
"./conf", // 当前目录下的conf
"../conf", // 上级目录下的conf
"../../conf", // 上上级目录下的conf
"../../../conf", // 上上上级目录下的conf
"../../../../conf", // 上上上上级目录下的conf
"conf", // 直接conf目录
}
// 添加配置文件的路径
for _, path := range configPaths {
viper.AddConfigPath(path)
}
// 如果设置了PROJECT_ROOT环境变量,也添加该路径
if projectRoot := os.Getenv("PROJECT_ROOT"); projectRoot != "" {
viper.AddConfigPath(filepath.Join(projectRoot, "conf"))
}
// 尝试读取配置文件
err = viper.ReadInConfig()
if err != nil {
// 如果还是找不到配置文件,尝试从项目根目录查找
// 通过查找go.mod文件来确定项目根目录
projectRoot := findProjectRoot(currentDir)
if projectRoot != "" {
viper.Reset()
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(filepath.Join(projectRoot, "conf"))
err = viper.ReadInConfig()
}
if err != nil {
log.Fatalf("Fail to parse 'conf/config.yml': %v", err)
}
}
}
// findProjectRoot 通过查找go.mod文件来确定项目根目录
func findProjectRoot(currentDir string) string {
dir := currentDir
for {
// 检查当前目录是否有go.mod文件
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
// 向上查找父目录
parent := filepath.Dir(dir)
if parent == dir {
// 已经到达根目录
break
}
dir = parent
}
return ""
}
================================================
FILE: utils/time_utils.go
================================================
package utils
import (
"DiTing-Go/global"
"strconv"
"time"
)
func TimestampStrToTimeStr(str *string) (*string, error) {
if str != nil && *str != "" {
// 时间戳转时间
timestamp, err := strconv.ParseInt(*str, 10, 64)
if err != nil {
global.Logger.Errorf("时间戳转换失败 %s", err)
return nil, err
}
cursor := time.Unix(0, timestamp)
cursorStr := cursor.Format(time.RFC3339Nano)
return &cursorStr, nil
}
return nil, nil
}
================================================
FILE: websocket/domain/enum/ws_type.go
================================================
package enum
const (
NewMessage = 4
)
================================================
FILE: websocket/domain/vo/resp/new_message_resp.go
================================================
package resp
type NewMessageResp struct {
Type int `json:"type"` // 消息类型
}
================================================
FILE: websocket/global/global.go
================================================
package global
import (
"github.com/gorilla/websocket"
cmap "github.com/orcaman/concurrent-map/v2"
"sync"
)
type Channels struct {
Uid int64
ChannelList []*websocket.Conn
Mu *sync.RWMutex
}
type User struct {
Uid int64
Channel *websocket.Conn
}
type Msg struct {
Uid int64
}
// UserChannelMap 用户和channel的映射
var UserChannelMap = cmap.New[*Channels]()
================================================
FILE: websocket/service/websocket_service.go
================================================
package service
import (
global2 "DiTing-Go/global"
"DiTing-Go/utils/jwt"
"DiTing-Go/websocket/global"
"fmt"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
"log"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
// TODO:连接断开处理
// 定义一个升级器,将普通的http连接升级为websocket连接
var upgrader = &websocket.Upgrader{
//定义读写缓冲区大小
WriteBufferSize: 1024,
ReadBufferSize: 1024,
//校验请求
CheckOrigin: func(r *http.Request) bool {
//如果不是get请求,返回错误
if r.Method != "GET" {
fmt.Println("请求方式错误")
return false
}
//还可以根据其他需求定制校验规则
return true
},
}
// Connect 建立WebSocket连接
func Connect(w http.ResponseWriter, r *http.Request) {
//先获得Http的token中的uid
//url中的获取token参数
params := r.URL.Query()
token := params.Get("token")
tokenInfo, err := jwt.ParseToken(token)
if err != nil {
global2.Logger.Errorf("无权限访问: %v", err)
return
}
uid := &tokenInfo.Uid
// Upgrade our raw HTTP connection to a websocket based one
conn, err := upgrader.Upgrade(w, r, nil)
// 关闭连接
defer conn.Close()
if err != nil {
log.Print("Error during connection upgradation:", err)
return
}
//连接成功后注册用户
// 将uid转换为string
stringUid := strconv.FormatInt(*uid, 10)
userChannel := global.Channels{
Uid: *uid,
ChannelList: make([]*websocket.Conn, 0),
Mu: new(sync.RWMutex),
}
user := global.User{
Uid: *uid,
Channel: conn,
}
global.UserChannelMap.Set(stringUid, &userChannel)
userChannelPtr, _ := global.UserChannelMap.Get(stringUid)
// TODO:加锁方式是否正确
// 将连接加入到用户的channel中
userChannelPtr.Mu.Lock()
userChannelPtr.ChannelList = append(userChannelPtr.ChannelList, conn)
userChannelPtr.Mu.Unlock()
// 定时发送心跳消息
go heatBeat(&user)
// 监听连接关闭事件
for {
_, _, err := conn.ReadMessage()
if err != nil {
disConnect(&user)
break
}
}
}
// Send 发送空消息代表有新消息,前端收到消息后再去后端拉取消息
func Send(uid int64, value []byte) error {
stringUid := strconv.FormatInt(uid, 10)
channels, _ := global.UserChannelMap.Get(stringUid)
// 用户不在线,直接返回
if channels == nil {
return nil
}
for _, conn := range channels.ChannelList {
// 发送空消息,代表有新消息
err := conn.WriteMessage(websocket.TextMessage, value)
if err != nil {
global2.Logger.Errorf("发送消息失败: %v", err)
return errors.New("Business Error")
}
}
return nil
}
// 移除连接
func disConnect(user *global.User) {
stringUid := strconv.FormatInt(user.Uid, 10)
conn := user.Channel
userChannel, _ := global.UserChannelMap.Get(stringUid)
userChannel.Mu.Lock()
for i, item := range userChannel.ChannelList {
if item == conn {
userChannel.ChannelList = append(userChannel.ChannelList[:i], userChannel.ChannelList[i+1:]...)
}
}
err := conn.Close()
if err != nil {
return
}
userChannel.Mu.Unlock()
}
// 解析jwt
func parseJwt(r *http.Request) (*int64, error) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
return nil, errors.New("无权限访问")
}
// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
return nil, errors.New("无权限访问")
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
token, err := jwt.ParseToken(parts[1])
if err != nil {
return nil, errors.New("无权限访问")
}
return &token.Uid, nil
}
// 心跳检测
func heatBeat(user *global.User) {
conn := user.Channel
// TODO:心跳时间从配置文件中读取
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
err := conn.WriteMessage(websocket.PingMessage, []byte("heartbeat"))
if err != nil {
log.Println(err)
return
}
// TODO:开发时关闭
//conn.SetReadDeadline(time.Now().Add(10 * time.Second))
conn.SetReadDeadline(time.Now().Add(24 * 360 * time.Hour))
_, _, err = conn.ReadMessage()
if err != nil {
disConnect(user)
log.Println("heartbeat response error:", err)
return
}
}
}
}