From 6b8fabb8d8eebc5bdf6f37cff8199537929d4b8b Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 02:01:23 +0800 Subject: [PATCH 1/7] nothing --- app/model/migrate/AutoMigrate.go | 19 ++++++++++--------- config/gorm_v2.yml | 3 ++- test/insertAnimal_test.go | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/model/migrate/AutoMigrate.go b/app/model/migrate/AutoMigrate.go index 2b14a0c..b46948f 100644 --- a/app/model/migrate/AutoMigrate.go +++ b/app/model/migrate/AutoMigrate.go @@ -141,17 +141,17 @@ func testInsertDeworming() { } func insertData() { - // testInsertSterilzation() - // fmt.Println("testInsertSterilzation success.") + testInsertSterilzation() + fmt.Println("testInsertSterilzation success.") - // testInsertBreed() - // fmt.Println("testInsertBreed success.") + testInsertBreed() + fmt.Println("testInsertBreed success.") - // testInsertStatus() - // fmt.Println("testInsertStatus success.") + testInsertStatus() + fmt.Println("testInsertStatus success.") - // testInsertAnmGender() - // fmt.Println("testInsertAnmGender success.") + testInsertAnmGender() + fmt.Println("testInsertAnmGender success.") testInsertVaccination() fmt.Println("testInsertVaccination success.") @@ -163,7 +163,8 @@ func insertData() { func main() { // 1. dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", - "root", "Havocantelope412#", "113.44.68.213", "3306", "hav_cats") // ATT MySQL + // "root", "Havocantelope412#", "113.44.68.213", "3306", "hav_cats") // ATT MySQL dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", + "root", "havocantelope412", "127.0.0.1", "3306", "hav_cats") // ATT MySQL fmt.Println("dsn:", dsn) dbMySQL, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { diff --git a/config/gorm_v2.yml b/config/gorm_v2.yml index 5c54597..409db62 100644 --- a/config/gorm_v2.yml +++ b/config/gorm_v2.yml @@ -5,7 +5,8 @@ Gormv2: # 只针对 gorm 操作数据库有效 IsInitGlobalGormMysql: 1 # 随项目启动为gorm db初始化一个全局 variable.GormDbMysql(完全等于*gorm.Db),正确配置数据库,该值必须设置为: 1 SlowThreshold: 30 # 慢 SQL 阈值(sql执行时间超过此时间单位(秒),就会触发系统日志记录) Write: - Host: "113.44.68.213" + # Host: "113.44.68.213" + Host: "127.0.0.1" DataBase: "hav_cats" Port: 3306 Prefix: "tb_" # 目前没有用到该配置项 diff --git a/test/insertAnimal_test.go b/test/insertAnimal_test.go index f053b69..b76a4d4 100644 --- a/test/insertAnimal_test.go +++ b/test/insertAnimal_test.go @@ -15,7 +15,8 @@ var DB *gorm.DB func Init() { dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", - "root", "Havocantelope412#", "113.44.68.213", "3306", "hav_cats") // danger MySQL + "root", "havocantelope412", "127.0.0.1", "3306", "hav_cats") // ATT MySQL + fmt.Println("dsn:", dsn) dbMySQL, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { From b7c7bf92d66b5a3ab1240968a0bace39afc52323 Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 11:31:40 +0800 Subject: [PATCH 2/7] fit Images Save with root Path --- app/http/controller/web/upload_controller.go | 2 +- app/http/validator/web/encounter/create.go | 1 + app/service/upload_file/upload_file.go | 3 ++- config/config.yml | 13 +++++++------ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/http/controller/web/upload_controller.go b/app/http/controller/web/upload_controller.go index c5bbd54..8950d9e 100644 --- a/app/http/controller/web/upload_controller.go +++ b/app/http/controller/web/upload_controller.go @@ -19,7 +19,7 @@ type Upload struct { func (u *Upload) StartUpload(context *gin.Context) { // TODO 如果之后要存储到 Linux 服务器上特殊路径下,就需要修改这里。 dir_name := context.GetString(consts.ValidatorPrefix + "dir_name") - savePath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), dir_name) + savePath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), dir_name) if r, finnalSavePath := upload_file.Upload(context, savePath); r == true { response.Success(context, consts.CurdStatusOkMsg, finnalSavePath) diff --git a/app/http/validator/web/encounter/create.go b/app/http/validator/web/encounter/create.go index 48bb6c5..3d60241 100644 --- a/app/http/validator/web/encounter/create.go +++ b/app/http/validator/web/encounter/create.go @@ -19,6 +19,7 @@ type Create struct { AnimalsId []int `form:"animals_id" json:"animals_id" binding:"required"` Title string `form:"title" json:"title" binding:"required"` Content string `form:"content" json:"content"` + Level int `form:"level" json:"level"` // Avatar string `form:"avatar" json:"avatar"` Photos []string `form:"photos" json:"photos"` // INFO 如果 Photo 为空,那就选取 Animals 的 Avatar diff --git a/app/service/upload_file/upload_file.go b/app/service/upload_file/upload_file.go index f23cebc..f93f0a7 100644 --- a/app/service/upload_file/upload_file.go +++ b/app/service/upload_file/upload_file.go @@ -44,8 +44,9 @@ func Upload(context *gin.Context, savePath string) (r bool, finnalSavePath inter } // 文件上传可以设置按照 xxx年-xx月 格式存储 +// INFO 但这个 returnPath 我还基本没有用到。 func generateYearMonthPath(savePathPre string) (string, string) { - returnPath := variable.BasePath + variable.ConfigYml.GetString("FileUploadSetting.UploadFileReturnPath") + returnPath := variable.BasePath + variable.ConfigYml.GetString("FileUploadSetting.UploadFileReturnPath") // UPDATE 因为没用到,所以就先不调整了。 curYearMonth := time.Now().In(time.Local).Format("2006_01") newSavePathPre := filepath.Join(savePathPre, curYearMonth) newReturnPathPre := filepath.Join(returnPath, curYearMonth) diff --git a/config/config.yml b/config/config.yml index dbfb499..4769dbc 100644 --- a/config/config.yml +++ b/config/config.yml @@ -35,7 +35,7 @@ Redis: Logs: GinLogName: "/store/logs/gin.log" #设置 gin 框架的接口访问日志 - GoSkeletonLogName: "/store/logs/skeleton.log" #设置GoSkeleton项目骨架运行时日志文件名,注意该名称不要与上一条重复 ,避免和 gin 框架的日志掺杂一起,造成混乱。 + GoSkeletonLogName: "/store/logs/skeleton.log" #设置GoSkeleton项目骨架运行时日志文件名,注意该名称不要与上一条重复 ,避免和 gin 框架的日志掺杂一起,造成混乱。 TextFormat: "json" #记录日志的格式,参数选项:console、json , console 表示一般的文本格式 TimePrecision: "millisecond" #记录日志时,相关的时间精度,该参数选项:second 、 millisecond , 分别表示 秒 和 毫秒 ,默认为毫秒级别 MaxSize: 10 #每个日志的最大尺寸(以MB为单位), 超过该值,系统将会自动进行切割 @@ -56,11 +56,12 @@ SnowFlake: SnowFlakeMachineId: 2 #如果本项目同时部署在多台机器,并且需要同时使用该算法,请为每一台机器设置不同的ID,区间范围: [0,1023] FileUploadSetting: - Size: 32 #设置上传文件的最大值,单位:M,注意: 如果go前置nginx服务器,nginx 默认限制文件上传大小为 50 M ,用户上传文件限制还需要继续修改 nginx 配置 - UploadFileField: "file" #post上传文件时,表单的键名 - UploadFileSavePath: "/public/nginx/" #上传文件保存在路径, 该路径与 BasePath 进行拼接使用 - UploadFileReturnPath: "" # 文件上后返回的路径,由于程序会自动创建软连接,自动将资源定位到实际路径,所有资源的访问入口建议都从public开始 - AllowMimeType: #允许的文件mime类型列表 + Size: 32 # 设置上传文件的最大值,单位:M,注意: 如果go前置nginx服务器,nginx 默认限制文件上传大小为 50 M ,用户上传文件限制还需要继续修改 nginx 配置 + UploadFileField: "file" # post上传文件时,表单的键名 + # TODO 为了方便后续兼容服务器上的 Nginx,直接采取绝对路径, + UploadFileSavePath: "D:/.File Data/GoProject/catface_back/public/nginx" # 上传文件保存在路径, 该路径与 BasePath 进行拼接使用 + UploadFileReturnPath: "" # 文件上后返回的路径,由于程序会自动创建软连接,自动将资源定位到实际路径,所有资源的访问入口建议都从public开始 + AllowMimeType: #允许的文件mime类型列表 - "image/jpeg" #jpg、jpeg图片格式 - "image/png" #png图片格式 # - "image/x-icon" #ico图片 From 44c9e825c92dd4b5b3a7234c812bdfd37e9e16c8 Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 12:13:50 +0800 Subject: [PATCH 3/7] fit #2 --- app/http/controller/web/animal_controller.go | 8 ++++---- app/http/controller/web/encounter_controller.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/http/controller/web/animal_controller.go b/app/http/controller/web/animal_controller.go index 7db75c5..cdfe6e8 100644 --- a/app/http/controller/web/animal_controller.go +++ b/app/http/controller/web/animal_controller.go @@ -154,8 +154,8 @@ func (a *Animals) Create(context *gin.Context) { avatar := photos[0] avatarWidth := variable.ConfigYml.GetFloat64("FileUploadSetting.AvatarWidth") - srcPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId, avatar) - dstPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsAvatar", avatar) + srcPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId, avatar) + dstPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsAvatar", avatar) avatarHeight, err := upload_file.ResizeImage(srcPath, dstPath, int(avatarWidth)) if err != nil { response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "") @@ -215,8 +215,8 @@ func (a *Animals) Create(context *gin.Context) { // STAGE-3 if anm_id, ok := model.CreateAnimalFactory("").InsertDate(context); ok { // 转移 photos 到 anm;采用 rename dir 的方式 - oldName := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId) - newName := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "anm_"+strconv.FormatInt(anm_id, 10)) + oldName := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId) + newName := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "anm_"+strconv.FormatInt(anm_id, 10)) err := os.Rename(oldName, newName) if err != nil { // TODO 特殊返回,成功了一半?或者需要清空原有的操作?不过感觉这一步几乎不会出错。 diff --git a/app/http/controller/web/encounter_controller.go b/app/http/controller/web/encounter_controller.go index 3af0674..93668bf 100644 --- a/app/http/controller/web/encounter_controller.go +++ b/app/http/controller/web/encounter_controller.go @@ -25,8 +25,8 @@ func (e *Encounters) Create(context *gin.Context) { avatar := photos[0] avatarWidth := variable.ConfigYml.GetFloat64("FileUploadSetting.AvatarWidth") - srcPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterPhotos", "hum_"+userId, avatar) - dstPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterAvatar", avatar) + srcPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterPhotos", "hum_"+userId, avatar) + dstPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterAvatar", avatar) avatarHeight, err := upload_file.ResizeImage(srcPath, dstPath, int(avatarWidth)) if err != nil { response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "") From a0afee41615d8da5cc047e2aee9f319852f572b4 Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 12:24:49 +0800 Subject: [PATCH 4/7] add a new errMsg --- app/global/errcode/encounter.go | 6 +++++- app/global/errcode/msg.go | 1 + app/http/controller/web/encounter_controller.go | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/global/errcode/encounter.go b/app/global/errcode/encounter.go index 8aeff76..85b45b3 100644 --- a/app/global/errcode/encounter.go +++ b/app/global/errcode/encounter.go @@ -6,4 +6,8 @@ const ( func EnocunterMsgInit(m msg) { m[ErrEaLinkInstert] = "路遇添加成功,但关联毛茸茸失败" -} \ No newline at end of file +} + +func EncounterMsgUserInit(m msg) { + m[ErrEaLinkInstert] = "路遇上传成功啦。但是不小心让毛茸茸跑丢了 /(ㄒoㄒ)/~~" +} diff --git a/app/global/errcode/msg.go b/app/global/errcode/msg.go index 75efcd2..9f6c2da 100644 --- a/app/global/errcode/msg.go +++ b/app/global/errcode/msg.go @@ -17,6 +17,7 @@ func init() { // INGO ErrMsgForUser = make(msg) AnimalMsgUserInit(ErrMsgForUser) + EncounterMsgUserInit(ErrMsgForUser) } func GeneralMsgInit(m msg) { diff --git a/app/http/controller/web/encounter_controller.go b/app/http/controller/web/encounter_controller.go index 93668bf..30d161a 100644 --- a/app/http/controller/web/encounter_controller.go +++ b/app/http/controller/web/encounter_controller.go @@ -68,10 +68,13 @@ func (e *Encounters) Create(context *gin.Context) { animals_id := data_transfer.GetFloat64Slice(context, "animals_id") // 由于是 Slice 就交给 EAlink 内部遍历时处理。 // Real Insert - 2: EA LINK if encounter_id, ok := model.CreateEncounterFactory("").InsertDate(context); ok && encounter_id > 0 { + // 2: EA Links if !model.CreateEncounterAnimalLinkFactory("").Insert(int64(encounter_id), animals_id) { - response.Fail(context, errcode.ErrEaLinkInstert, errcode.ErrMsg[errcode.ErrEaLinkInstert], "") + response.Fail(context, errcode.ErrEaLinkInstert, errcode.ErrMsg[errcode.ErrEaLinkInstert], errcode.ErrMsgForUser[errcode.ErrEaLinkInstert]) return } + // 3. ES speed + // TODO response.Success(context, consts.CurdStatusOkMsg, gin.H{ "encounter_id": encounter_id, From 5f0e76c0bbb935fa243e512b1d08bec72e39111b Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 12:41:57 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=86=95real=20link=20es=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/global/my_errors/my_errors.go | 3 +++ app/global/variable/variable.go | 5 ++++- app/http/controller/web/encounter_controller.go | 4 +++- bootstrap/init.go | 9 +++++++++ config/config.yml | 5 +++++ test/es/init.go | 2 +- 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/global/my_errors/my_errors.go b/app/global/my_errors/my_errors.go index c3efa00..6e5ee35 100644 --- a/app/global/my_errors/my_errors.go +++ b/app/global/my_errors/my_errors.go @@ -71,4 +71,7 @@ const ( // GLM 部分 ErrorsGlmClientInitFail string = "GLM Client 初始化失败" + + // ES 部分 + ErrorsInitConnFail string = "初始化 Elasticsearch 连接失败" ) diff --git a/app/global/variable/variable.go b/app/global/variable/variable.go index 08d8beb..c963c74 100644 --- a/app/global/variable/variable.go +++ b/app/global/variable/variable.go @@ -9,10 +9,10 @@ import ( "strings" "github.com/casbin/casbin/v2" + "github.com/elastic/go-elasticsearch/v8" "github.com/yankeguo/zhipu" "go.uber.org/zap" "gorm.io/gorm" - ) var ( @@ -46,6 +46,9 @@ var ( // GLM 全局客户端 GlmClient *zhipu.Client + + // ES 全局客户端 + ElasticClient *elasticsearch.Client ) func init() { diff --git a/app/http/controller/web/encounter_controller.go b/app/http/controller/web/encounter_controller.go index 30d161a..45bbb6f 100644 --- a/app/http/controller/web/encounter_controller.go +++ b/app/http/controller/web/encounter_controller.go @@ -74,7 +74,9 @@ func (e *Encounters) Create(context *gin.Context) { return } // 3. ES speed - // TODO + if level := int(context.GetFloat64(consts.ValidatorPrefix + "level")); level > 1 { + // TODO + } response.Success(context, consts.CurdStatusOkMsg, gin.H{ "encounter_id": encounter_id, diff --git a/bootstrap/init.go b/bootstrap/init.go index 5be6992..862fddd 100644 --- a/bootstrap/init.go +++ b/bootstrap/init.go @@ -16,6 +16,7 @@ import ( "log" "os" + "github.com/elastic/go-elasticsearch/v8" "github.com/yankeguo/zhipu" ) @@ -119,4 +120,12 @@ func init() { if err != nil { log.Fatal(my_errors.ErrorsGlmClientInitFail + err.Error()) } + + // 12. ES 客户端启动 + variable.ElasticClient, err = elasticsearch.NewClient(elasticsearch.Config{ + Addresses: []string{variable.ConfigYml.GetString("ElasticSearch.Addr")}, + }) + if err != nil { + log.Fatal(my_errors.ErrorsInitConnFail + err.Error()) + } } diff --git a/config/config.yml b/config/config.yml index 4769dbc..c5dddae 100644 --- a/config/config.yml +++ b/config/config.yml @@ -155,4 +155,9 @@ Weixin: Glm: ApiKey: "0cf510ebc01599dba2a593069c1bdfbc.nQBQ4skP8xBh7ijU" DefaultModel: "glm-4-flash" + +ElasticSearch: + Addr: "http://localhost:9200" + UserName: "elastic" + Password: "" \ No newline at end of file diff --git a/test/es/init.go b/test/es/init.go index 00560d4..9890b1f 100644 --- a/test/es/init.go +++ b/test/es/init.go @@ -13,7 +13,7 @@ func InitElastic() { var err error ElasticClient, err = elasticsearch.NewClient(elasticsearch.Config{ // Addresses: []string{"http://113.44.68.213:9200"}, - Addresses: []string{"http://127.0.0.1:9200"}, + Addresses: []string{"http://localhost:9200"}, // TIP 用 localhost 可以,127.0.0.1 无效... // Username: "elastic", // Password: "U8n61yn*Sp4Kvbuqo_K8", }) From 7ab3cfa81e9c2944839b199662f88939c094b7b2 Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 15:53:47 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=86=95add=20encounter=20insert=20with?= =?UTF-8?q?=20ES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/global/variable/variable.go | 17 ++ .../controller/web/encounter_controller.go | 19 +- app/model/encounter.go | 12 +- app/model_es/encounter.go | 220 ++++++++++++++++++ test/es/encounter_test.go | 50 ++++ 5 files changed, 306 insertions(+), 12 deletions(-) create mode 100644 app/model_es/encounter.go create mode 100644 test/es/encounter_test.go diff --git a/app/global/variable/variable.go b/app/global/variable/variable.go index c963c74..355b1cc 100644 --- a/app/global/variable/variable.go +++ b/app/global/variable/variable.go @@ -64,3 +64,20 @@ func init() { log.Fatal(my_errors.ErrorsBasePath) } } + +func init() { + // 1. 初始化程序根目录 + if curPath, err := os.Getwd(); err == nil { + // 路径进行处理,兼容单元测试程序启动时的奇怪路径 + if len(os.Args) > 1 && strings.HasPrefix(os.Args[1], "-test") { + // 替换 \ 为 /,然后移除 /test 及其后的内容 + curPath = strings.ReplaceAll(curPath, "\\", "/") + parts := strings.Split(curPath, "/test") + BasePath = parts[0] + } else { + BasePath = curPath + } + } else { + log.Fatal(my_errors.ErrorsBasePath) + } +} diff --git a/app/http/controller/web/encounter_controller.go b/app/http/controller/web/encounter_controller.go index 45bbb6f..72111c3 100644 --- a/app/http/controller/web/encounter_controller.go +++ b/app/http/controller/web/encounter_controller.go @@ -6,6 +6,7 @@ import ( "catface/app/global/variable" "catface/app/http/validator/core/data_transfer" "catface/app/model" + "catface/app/model_es" "catface/app/service/encounter/curd" "catface/app/service/upload_file" "catface/app/utils/response" @@ -67,19 +68,21 @@ func (e *Encounters) Create(context *gin.Context) { // STAGE -3: Real Insert - 1: ENC animals_id := data_transfer.GetFloat64Slice(context, "animals_id") // 由于是 Slice 就交给 EAlink 内部遍历时处理。 // Real Insert - 2: EA LINK - if encounter_id, ok := model.CreateEncounterFactory("").InsertDate(context); ok && encounter_id > 0 { - // 2: EA Links - if !model.CreateEncounterAnimalLinkFactory("").Insert(int64(encounter_id), animals_id) { - response.Fail(context, errcode.ErrEaLinkInstert, errcode.ErrMsg[errcode.ErrEaLinkInstert], errcode.ErrMsgForUser[errcode.ErrEaLinkInstert]) - return - } + if encounter, ok := model.CreateEncounterFactory("").InsertDate(context); ok { + // 2: EA Links; // TIP 感觉直接使用 go 会直接且清晰。 + go model.CreateEncounterAnimalLinkFactory("").Insert(encounter.Id, animals_id) + // if !model.CreateEncounterAnimalLinkFactory("").Insert(int64(encounter_id), animals_id) { + // response.Fail(context, errcode.ErrEaLinkInstert, errcode.ErrMsg[errcode.ErrEaLinkInstert], errcode.ErrMsgForUser[errcode.ErrEaLinkInstert]) + // return + // } + // 3. ES speed if level := int(context.GetFloat64(consts.ValidatorPrefix + "level")); level > 1 { - // TODO + go model_es.CreateEncounterESFactory(&encounter).InsertDocument() } response.Success(context, consts.CurdStatusOkMsg, gin.H{ - "encounter_id": encounter_id, + "encounter_id": encounter.Id, }) } else { response.Fail(context, consts.CurdCreatFailCode, consts.CurdCreatFailMsg+", 新增错误", "") diff --git a/app/model/encounter.go b/app/model/encounter.go index 9e4303d..e007da1 100644 --- a/app/model/encounter.go +++ b/app/model/encounter.go @@ -44,18 +44,22 @@ func (e *Encounter) TableName() string { return "encounters" } -func (e *Encounter) InsertDate(c *gin.Context) (int64, bool) { - var tmp Encounter +/** + * @description: + * @param {*gin.Context} c + * @return {*} 返回创建的绑定对象,之后 model_es 的利用。 + */ +func (e *Encounter) InsertDate(c *gin.Context) (tmp Encounter, ok bool) { if err := data_bind.ShouldBindFormDataToModel(c, &tmp); err == nil { if res := e.Create(&tmp); res.Error == nil { - return tmp.Id, true + return tmp, true } else { variable.ZapLog.Error("Encounter 数据新增出错", zap.Error(res.Error)) } } else { variable.ZapLog.Error("Encounter 数据绑定出错", zap.Error(err)) } - return 0, false + return tmp, false } func formatEncounterList(rows *gorm.DB) (temp []EncounterList, err error) { diff --git a/app/model_es/encounter.go b/app/model_es/encounter.go new file mode 100644 index 0000000..bc877a2 --- /dev/null +++ b/app/model_es/encounter.go @@ -0,0 +1,220 @@ +package model_es + +import ( + "bytes" + "catface/app/global/variable" + "catface/app/model" + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/elastic/go-elasticsearch/v8" + "github.com/elastic/go-elasticsearch/v8/esapi" +) + +func CreateEncounterESFactory(encounter *model.Encounter) *Encounter { + // 我把数值绑定到了工厂创建当中。 + return &Encounter{ + Id: encounter.Id, + Title: encounter.Title, + Content: encounter.Content, + Tags: encounter.TagsSlice, // TODO 暂时没有对此字段的查询。 + } +} + +// INFO 存储能够作为索引存在的数据。 +type Encounter struct { + Id int64 `json:"id"` + Title string `json:"title"` + Content string `json:"content"` + Tags []string `json:"tags"` +} + +func (e *Encounter) IndexName() string { + return "catface_encounters" +} + +func (e *Encounter) InsertDocument() error { + ctx := context.Background() + + // 将结构体转换为 JSON 字符串 + data, err := json.Marshal(e) + if err != nil { + return err + } + + // 创建请求 + req := esapi.IndexRequest{ + Index: e.IndexName(), + DocumentID: fmt.Sprintf("%d", e.Id), + Body: bytes.NewReader(data), + Refresh: "true", + } + + // 发送请求 + res, err := req.Do(ctx, variable.ElasticClient) + if err != nil { + return err + } + defer res.Body.Close() + + if res.IsError() { + var e map[string]interface{} + if err := json.NewDecoder(res.Body).Decode(&e); err != nil { + return fmt.Errorf("error parsing the response body: %s", err) + } else { + return fmt.Errorf("[%s] %s: %s", + res.Status(), + e["error"].(map[string]interface{})["type"], + e["error"].(map[string]interface{})["reason"], + ) + } + } + + return nil +} + +func (e *Encounter) UpdateDocument(client *elasticsearch.Client, encounter *Encounter) error { + ctx := context.Background() + + // 将结构体转换为 JSON 字符串 + data, err := json.Marshal(map[string]interface{}{ + "doc": encounter, + }) + if err != nil { + return err + } + + // 创建请求 + req := esapi.UpdateRequest{ + Index: encounter.IndexName(), + DocumentID: fmt.Sprintf("%d", encounter.Id), + Body: bytes.NewReader(data), + Refresh: "true", + } + + // 发送请求 + res, err := req.Do(ctx, client) + if err != nil { + return err + } + defer res.Body.Close() + + if res.IsError() { + var e map[string]interface{} + if err := json.NewDecoder(res.Body).Decode(&e); err != nil { + return fmt.Errorf("error parsing the response body: %s", err) + } else { + return fmt.Errorf("[%s] %s: %s", + res.Status(), + e["error"].(map[string]interface{})["type"], + e["error"].(map[string]interface{})["reason"], + ) + } + } + + return nil +} + +/** + * @description: 粗略地包含各种关键词匹配, + * @param {*elasticsearch.Client} client + * @param {string} query + * @return {*} 对应 Encounter 的 id,然后交给 MySQL 来查询详细的信息? + */ +func (e *Encounter) QueryDocumentsMatchAll(client *elasticsearch.Client, query string) ([]int64, error) { + ctx := context.Background() + + // 创建查询请求 + req := esapi.SearchRequest{ // UPDATE 同时实现查询高亮? + Index: []string{e.IndexName()}, + Body: strings.NewReader(fmt.Sprintf(`{ + "_source": ["id"], + "query": { + "bool": { + "should": [ + { + "match": { + "title": "%s" + } + }, + { + "match": { + "content": "%s" + } + } + ] + } + } + }`, query, query)), + } + + // 发送请求 + res, err := req.Do(ctx, client) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.IsError() { + var e map[string]interface{} + if err := json.NewDecoder(res.Body).Decode(&e); err != nil { + return nil, fmt.Errorf("error parsing the response body: %s", err) + } else { + return nil, fmt.Errorf("[%s] %s: %s", + res.Status(), + e["error"].(map[string]interface{})["type"], + e["error"].(map[string]interface{})["reason"], + ) + } + } + + // 解析响应 + var r map[string]interface{} + if err := json.NewDecoder(res.Body).Decode(&r); err != nil { + return nil, err + } + + // 提取命中结果 + hits, ok := r["hits"].(map[string]interface{})["hits"].([]interface{}) + if !ok { + return nil, fmt.Errorf("error extracting hits from response") + } + + fmt.Println(hits) + + // 转换为 id 切片 + var ids []int64 + for _, hit := range hits { + hitMap := hit.(map[string]interface{})["_source"].(map[string]interface{}) + id := int64(hitMap["id"].(float64)) + ids = append(ids, id) + } + + return ids, nil + + // // 转换为 Encounter 切片 + // var encounters []*Encounter + // for _, hit := range hits { + // hitMap := hit.(map[string]interface{}) + // source := hitMap["_source"].(map[string]interface{}) + + // // TIP 将 []interface{} 转换为 []string + // tagsInterface := source["tags"].([]interface{}) + // tags := make([]string, len(tagsInterface)) + // for i, tag := range tagsInterface { + // tags[i] = tag.(string) + // } + + // encounter := &Encounter{ + // Id: int64(source["id"].(float64)), + // Title: source["title"].(string), + // Content: source["content"].(string), + // Tags: tags, + // } + // encounters = append(encounters, encounter) + // } + + // return encounters, nil +} diff --git a/test/es/encounter_test.go b/test/es/encounter_test.go new file mode 100644 index 0000000..3eaac97 --- /dev/null +++ b/test/es/encounter_test.go @@ -0,0 +1,50 @@ +package test + +import ( + "catface/app/global/variable" + "catface/app/model" + "catface/app/model_es" + _ "catface/bootstrap" + "fmt" + "testing" +) + +func TestEncounterEs(t *testing.T) { + // 示例数据 + encounterOri := &model.Encounter{ + BaseModel: model.BaseModel{ + Id: 4, + }, + Title: "猪皮伤势轻,需静养猪皮伤势轻,需静养", + Content: "猪皮被带到医院检查了,拍片结果显示损伤不严重,静养即可自愈。建议这段时间不要折腾他,让老登好好休息。", + TagsSlice: []string{"猪皮", "脚伤", "骗保"}, + } + + encounter := model_es.CreateEncounterESFactory(encounterOri) + + // 插入文档 + // if err := encounter.InsertDocument(); err != nil { + // t.Fatalf("插入文档时出错: %s", err) + // } + go encounter.InsertDocument() + + // // 更新文档 + // encounter.Content = "更新: 猪皮被带到医院检查了,拍片结果显示损伤不严重,静养即可自愈。建议这段时间不要折腾他,让老登好好休息。" + // if err := encounter.UpdateDocument(variable.ElasticClient, encounter); err != nil { + // t.Fatalf("更新文档时出错: %s", err) + // } + + // 查询文档 + encounters, err := encounter.QueryDocumentsMatchAll(variable.ElasticClient, "猪皮") + if err != nil { + t.Fatalf("查询文档时出错: %s", err) + } + + // for _, e := range encounters { + // fmt.Printf("ID: %d, 标题: %s, 内容: %s, 标签: %v\n", e.Id, e.Title, e.Content, e.Tags) + // } + + for _, e := range encounters { + fmt.Printf("ID: %d\n", e) + } +} From 5cf79f95d9b5c0cce8e602b29b3a67cf86ef931f Mon Sep 17 00:00:00 2001 From: Havoc412 <2993167370@qq.com> Date: Tue, 12 Nov 2024 16:39:06 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=8E=8F=20finish=20search=20all=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/http/controller/web/search_controller.go | 40 +++++++++++++++++++ .../web_register_validator.go | 5 +++ app/http/validator/web/search/searchAll.go | 30 ++++++++++++++ app/model/encounter.go | 14 +++++++ app/model_es/encounter.go | 9 ++++- routers/web.go | 5 +++ 6 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 app/http/controller/web/search_controller.go create mode 100644 app/http/validator/web/search/searchAll.go diff --git a/app/http/controller/web/search_controller.go b/app/http/controller/web/search_controller.go new file mode 100644 index 0000000..13a8e25 --- /dev/null +++ b/app/http/controller/web/search_controller.go @@ -0,0 +1,40 @@ +package web + +import ( + "catface/app/global/consts" + "catface/app/model" + "catface/app/model_es" + "catface/app/utils/response" + + "github.com/gin-gonic/gin" +) + +type Search struct { +} + +/** + * @description: 全局搜索:AnmName + Encounter + * @param {*gin.Context} context + * @return {*} + */ +func (s *Search) SearchAll(context *gin.Context) { + query := context.GetString(consts.ValidatorPrefix + "query") + + var animals []model.Animal + var encounters []model.Encounter + + // 1. Animal Name // TODO 增加字段的过滤,看前端了。 + animals = model.CreateAnimalFactory("").ShowByName(query) + + // 2. Encounter + encounterIds, _ := model_es.CreateEncounterESFactory(nil).QueryDocumentsMatchAll(query) + + if len(encounterIds) > 0 { + encounters = model.CreateEncounterFactory("").ShowByIDs(encounterIds) + } + + response.Success(context, consts.CurdStatusOkMsg, gin.H{ + "animals": animals, + "encounters": encounters, + }) +} diff --git a/app/http/validator/common/register_validator/web_register_validator.go b/app/http/validator/common/register_validator/web_register_validator.go index 7d1dcbb..0cd56a5 100644 --- a/app/http/validator/common/register_validator/web_register_validator.go +++ b/app/http/validator/common/register_validator/web_register_validator.go @@ -10,6 +10,7 @@ import ( "catface/app/http/validator/web/encounter" "catface/app/http/validator/web/encounter_like" "catface/app/http/validator/web/nlp" + "catface/app/http/validator/web/search" "catface/app/http/validator/web/users" ) @@ -81,4 +82,8 @@ func WebRegisterValidator() { // TAG NLP key = consts.ValidatorPrefix + "NlpTitle" containers.Set(key, nlp.Title{}) + + // TAG Search + key = consts.ValidatorPrefix + "SearchAll" + containers.Set(key, search.SearchAll{}) } diff --git a/app/http/validator/web/search/searchAll.go b/app/http/validator/web/search/searchAll.go new file mode 100644 index 0000000..a4b4c09 --- /dev/null +++ b/app/http/validator/web/search/searchAll.go @@ -0,0 +1,30 @@ +package search + +import ( + "catface/app/global/consts" + "catface/app/http/controller/web" + "catface/app/http/validator/core/data_transfer" + "catface/app/utils/response" + + "github.com/gin-gonic/gin" +) + +type SearchAll struct { + Query string `form:"query" json:"query"` +} + +func (s SearchAll) CheckParams(context *gin.Context) { + if err := context.ShouldBind(&s); err != nil { + response.ValidatorError(context, err) + return + } + + extraAddBindDataContext := data_transfer.DataAddContext(s, consts.ValidatorPrefix, context) + if extraAddBindDataContext == nil { + response.ErrorSystem(context, "Animal Create 表单验证器json化失败", "") + } else { + // 验证完成,调用控制器,并将验证器成员(字段)递给控制器,保持上下文数据一致性 + (&web.Search{}).SearchAll(extraAddBindDataContext) + } + +} diff --git a/app/model/encounter.go b/app/model/encounter.go index e007da1..8904ff3 100644 --- a/app/model/encounter.go +++ b/app/model/encounter.go @@ -173,6 +173,20 @@ func (e *Encounter) ShowByID(id int64) (temp *Encounter, err error) { // return } +func (e *Encounter) ShowByIDs(ids []int64, attrs ...string) (temp []Encounter) { + db := e.DB.Table(e.TableName()) + + if len(attrs) > 0 { + db = db.Select(attrs) + } + + err := db.Where("id in (?)", ids).Find(&temp).Error + if err != nil { + variable.ZapLog.Error("Encounter ShowByIDs Error", zap.Error(err)) + } + return +} + /** * @description: 过去 1 个月,发送过路遇表的 ids,同时去重。 * @param {*} user_id diff --git a/app/model_es/encounter.go b/app/model_es/encounter.go index bc877a2..3439cdf 100644 --- a/app/model_es/encounter.go +++ b/app/model_es/encounter.go @@ -14,6 +14,10 @@ import ( ) func CreateEncounterESFactory(encounter *model.Encounter) *Encounter { + if encounter == nil { // UPDATE 这样写好丑。 + return &Encounter{} + } + // 我把数值绑定到了工厂创建当中。 return &Encounter{ Id: encounter.Id, @@ -75,6 +79,7 @@ func (e *Encounter) InsertDocument() error { return nil } +// TODO 改正,仿 Insert func (e *Encounter) UpdateDocument(client *elasticsearch.Client, encounter *Encounter) error { ctx := context.Background() @@ -123,7 +128,7 @@ func (e *Encounter) UpdateDocument(client *elasticsearch.Client, encounter *Enco * @param {string} query * @return {*} 对应 Encounter 的 id,然后交给 MySQL 来查询详细的信息? */ -func (e *Encounter) QueryDocumentsMatchAll(client *elasticsearch.Client, query string) ([]int64, error) { +func (e *Encounter) QueryDocumentsMatchAll(query string) ([]int64, error) { ctx := context.Background() // 创建查询请求 @@ -151,7 +156,7 @@ func (e *Encounter) QueryDocumentsMatchAll(client *elasticsearch.Client, query s } // 发送请求 - res, err := req.Do(ctx, client) + res, err := req.Do(ctx, variable.ElasticClient) if err != nil { return nil, err } diff --git a/routers/web.go b/routers/web.go index 01ae01b..ab59863 100644 --- a/routers/web.go +++ b/routers/web.go @@ -149,6 +149,11 @@ func InitWebRouter() *gin.Engine { { nlp.POST("title", validatorFactory.Create(consts.ValidatorPrefix+"NlpTitle")) } + + search := backend.Group("search") + { + search.GET("", validatorFactory.Create(consts.ValidatorPrefix+"SearchAll")) + } // } }