Merge branch 'main' into for-dev
This commit is contained in:
commit
76285dc0e4
@ -7,3 +7,7 @@ const (
|
|||||||
func EnocunterMsgInit(m msg) {
|
func EnocunterMsgInit(m msg) {
|
||||||
m[ErrEaLinkInstert] = "路遇添加成功,但关联毛茸茸失败"
|
m[ErrEaLinkInstert] = "路遇添加成功,但关联毛茸茸失败"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncounterMsgUserInit(m msg) {
|
||||||
|
m[ErrEaLinkInstert] = "路遇上传成功啦。但是不小心让毛茸茸跑丢了 /(ㄒoㄒ)/~~"
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@ func init() {
|
|||||||
// INGO
|
// INGO
|
||||||
ErrMsgForUser = make(msg)
|
ErrMsgForUser = make(msg)
|
||||||
AnimalMsgUserInit(ErrMsgForUser)
|
AnimalMsgUserInit(ErrMsgForUser)
|
||||||
|
EncounterMsgUserInit(ErrMsgForUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GeneralMsgInit(m msg) {
|
func GeneralMsgInit(m msg) {
|
||||||
|
@ -71,4 +71,7 @@ const (
|
|||||||
|
|
||||||
// GLM 部分
|
// GLM 部分
|
||||||
ErrorsGlmClientInitFail string = "GLM Client 初始化失败"
|
ErrorsGlmClientInitFail string = "GLM Client 初始化失败"
|
||||||
|
|
||||||
|
// ES 部分
|
||||||
|
ErrorsInitConnFail string = "初始化 Elasticsearch 连接失败"
|
||||||
)
|
)
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casbin/casbin/v2"
|
"github.com/casbin/casbin/v2"
|
||||||
|
"github.com/elastic/go-elasticsearch/v8"
|
||||||
"github.com/yankeguo/zhipu"
|
"github.com/yankeguo/zhipu"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -46,6 +46,9 @@ var (
|
|||||||
|
|
||||||
// GLM 全局客户端
|
// GLM 全局客户端
|
||||||
GlmClient *zhipu.Client
|
GlmClient *zhipu.Client
|
||||||
|
|
||||||
|
// ES 全局客户端
|
||||||
|
ElasticClient *elasticsearch.Client
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -61,3 +64,20 @@ func init() {
|
|||||||
log.Fatal(my_errors.ErrorsBasePath)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -154,8 +154,8 @@ func (a *Animals) Create(context *gin.Context) {
|
|||||||
avatar := photos[0]
|
avatar := photos[0]
|
||||||
avatarWidth := variable.ConfigYml.GetFloat64("FileUploadSetting.AvatarWidth")
|
avatarWidth := variable.ConfigYml.GetFloat64("FileUploadSetting.AvatarWidth")
|
||||||
|
|
||||||
srcPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId, avatar)
|
srcPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId, avatar)
|
||||||
dstPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsAvatar", avatar)
|
dstPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsAvatar", avatar)
|
||||||
avatarHeight, err := upload_file.ResizeImage(srcPath, dstPath, int(avatarWidth))
|
avatarHeight, err := upload_file.ResizeImage(srcPath, dstPath, int(avatarWidth))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "")
|
response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "")
|
||||||
@ -215,8 +215,8 @@ func (a *Animals) Create(context *gin.Context) {
|
|||||||
// STAGE-3
|
// STAGE-3
|
||||||
if anm_id, ok := model.CreateAnimalFactory("").InsertDate(context); ok {
|
if anm_id, ok := model.CreateAnimalFactory("").InsertDate(context); ok {
|
||||||
// 转移 photos 到 anm;采用 rename dir 的方式
|
// 转移 photos 到 anm;采用 rename dir 的方式
|
||||||
oldName := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "hum_"+userId)
|
oldName := filepath.Join(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))
|
newName := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "catsPhotos", "anm_"+strconv.FormatInt(anm_id, 10))
|
||||||
err := os.Rename(oldName, newName)
|
err := os.Rename(oldName, newName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO 特殊返回,成功了一半?或者需要清空原有的操作?不过感觉这一步几乎不会出错。
|
// TODO 特殊返回,成功了一半?或者需要清空原有的操作?不过感觉这一步几乎不会出错。
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"catface/app/global/variable"
|
"catface/app/global/variable"
|
||||||
"catface/app/http/validator/core/data_transfer"
|
"catface/app/http/validator/core/data_transfer"
|
||||||
"catface/app/model"
|
"catface/app/model"
|
||||||
|
"catface/app/model_es"
|
||||||
"catface/app/service/encounter/curd"
|
"catface/app/service/encounter/curd"
|
||||||
"catface/app/service/upload_file"
|
"catface/app/service/upload_file"
|
||||||
"catface/app/utils/response"
|
"catface/app/utils/response"
|
||||||
@ -25,8 +26,8 @@ func (e *Encounters) Create(context *gin.Context) {
|
|||||||
avatar := photos[0]
|
avatar := photos[0]
|
||||||
avatarWidth := variable.ConfigYml.GetFloat64("FileUploadSetting.AvatarWidth")
|
avatarWidth := variable.ConfigYml.GetFloat64("FileUploadSetting.AvatarWidth")
|
||||||
|
|
||||||
srcPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterPhotos", "hum_"+userId, avatar)
|
srcPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterPhotos", "hum_"+userId, avatar)
|
||||||
dstPath := filepath.Join(variable.BasePath, variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterAvatar", avatar)
|
dstPath := filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), "encounterAvatar", avatar)
|
||||||
avatarHeight, err := upload_file.ResizeImage(srcPath, dstPath, int(avatarWidth))
|
avatarHeight, err := upload_file.ResizeImage(srcPath, dstPath, int(avatarWidth))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "")
|
response.Fail(context, consts.FilesUploadFailCode, consts.FilesUploadFailMsg, "")
|
||||||
@ -67,14 +68,21 @@ func (e *Encounters) Create(context *gin.Context) {
|
|||||||
// STAGE -3: Real Insert - 1: ENC
|
// STAGE -3: Real Insert - 1: ENC
|
||||||
animals_id := data_transfer.GetFloat64Slice(context, "animals_id") // 由于是 Slice 就交给 EAlink 内部遍历时处理。
|
animals_id := data_transfer.GetFloat64Slice(context, "animals_id") // 由于是 Slice 就交给 EAlink 内部遍历时处理。
|
||||||
// Real Insert - 2: EA LINK
|
// Real Insert - 2: EA LINK
|
||||||
if encounter_id, ok := model.CreateEncounterFactory("").InsertDate(context); ok && encounter_id > 0 {
|
if encounter, ok := model.CreateEncounterFactory("").InsertDate(context); ok {
|
||||||
if !model.CreateEncounterAnimalLinkFactory("").Insert(int64(encounter_id), animals_id) {
|
// 2: EA Links; // TIP 感觉直接使用 go 会直接且清晰。
|
||||||
response.Fail(context, errcode.ErrEaLinkInstert, errcode.ErrMsg[errcode.ErrEaLinkInstert], "")
|
go model.CreateEncounterAnimalLinkFactory("").Insert(encounter.Id, animals_id)
|
||||||
return
|
// 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 {
|
||||||
|
go model_es.CreateEncounterESFactory(&encounter).InsertDocument()
|
||||||
}
|
}
|
||||||
|
|
||||||
response.Success(context, consts.CurdStatusOkMsg, gin.H{
|
response.Success(context, consts.CurdStatusOkMsg, gin.H{
|
||||||
"encounter_id": encounter_id,
|
"encounter_id": encounter.Id,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
response.Fail(context, consts.CurdCreatFailCode, consts.CurdCreatFailMsg+", 新增错误", "")
|
response.Fail(context, consts.CurdCreatFailCode, consts.CurdCreatFailMsg+", 新增错误", "")
|
||||||
|
40
app/http/controller/web/search_controller.go
Normal file
40
app/http/controller/web/search_controller.go
Normal file
@ -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,
|
||||||
|
})
|
||||||
|
}
|
@ -19,7 +19,7 @@ type Upload struct {
|
|||||||
func (u *Upload) StartUpload(context *gin.Context) {
|
func (u *Upload) StartUpload(context *gin.Context) {
|
||||||
// TODO 如果之后要存储到 Linux 服务器上特殊路径下,就需要修改这里。
|
// TODO 如果之后要存储到 Linux 服务器上特殊路径下,就需要修改这里。
|
||||||
dir_name := context.GetString(consts.ValidatorPrefix + "dir_name")
|
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 {
|
if r, finnalSavePath := upload_file.Upload(context, savePath); r == true {
|
||||||
response.Success(context, consts.CurdStatusOkMsg, finnalSavePath)
|
response.Success(context, consts.CurdStatusOkMsg, finnalSavePath)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"catface/app/http/validator/web/encounter"
|
"catface/app/http/validator/web/encounter"
|
||||||
"catface/app/http/validator/web/encounter_like"
|
"catface/app/http/validator/web/encounter_like"
|
||||||
"catface/app/http/validator/web/nlp"
|
"catface/app/http/validator/web/nlp"
|
||||||
|
"catface/app/http/validator/web/search"
|
||||||
"catface/app/http/validator/web/users"
|
"catface/app/http/validator/web/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,4 +82,8 @@ func WebRegisterValidator() {
|
|||||||
// TAG NLP
|
// TAG NLP
|
||||||
key = consts.ValidatorPrefix + "NlpTitle"
|
key = consts.ValidatorPrefix + "NlpTitle"
|
||||||
containers.Set(key, nlp.Title{})
|
containers.Set(key, nlp.Title{})
|
||||||
|
|
||||||
|
// TAG Search
|
||||||
|
key = consts.ValidatorPrefix + "SearchAll"
|
||||||
|
containers.Set(key, search.SearchAll{})
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ type Create struct {
|
|||||||
AnimalsId []int `form:"animals_id" json:"animals_id" binding:"required"`
|
AnimalsId []int `form:"animals_id" json:"animals_id" binding:"required"`
|
||||||
Title string `form:"title" json:"title" binding:"required"`
|
Title string `form:"title" json:"title" binding:"required"`
|
||||||
Content string `form:"content" json:"content"`
|
Content string `form:"content" json:"content"`
|
||||||
|
Level int `form:"level" json:"level"`
|
||||||
|
|
||||||
// Avatar string `form:"avatar" json:"avatar"`
|
// Avatar string `form:"avatar" json:"avatar"`
|
||||||
Photos []string `form:"photos" json:"photos"` // INFO 如果 Photo 为空,那就选取 Animals 的 Avatar
|
Photos []string `form:"photos" json:"photos"` // INFO 如果 Photo 为空,那就选取 Animals 的 Avatar
|
||||||
|
30
app/http/validator/web/search/searchAll.go
Normal file
30
app/http/validator/web/search/searchAll.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -44,18 +44,22 @@ func (e *Encounter) TableName() string {
|
|||||||
return "encounters"
|
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 err := data_bind.ShouldBindFormDataToModel(c, &tmp); err == nil {
|
||||||
if res := e.Create(&tmp); res.Error == nil {
|
if res := e.Create(&tmp); res.Error == nil {
|
||||||
return tmp.Id, true
|
return tmp, true
|
||||||
} else {
|
} else {
|
||||||
variable.ZapLog.Error("Encounter 数据新增出错", zap.Error(res.Error))
|
variable.ZapLog.Error("Encounter 数据新增出错", zap.Error(res.Error))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
variable.ZapLog.Error("Encounter 数据绑定出错", zap.Error(err))
|
variable.ZapLog.Error("Encounter 数据绑定出错", zap.Error(err))
|
||||||
}
|
}
|
||||||
return 0, false
|
return tmp, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatEncounterList(rows *gorm.DB) (temp []EncounterList, err error) {
|
func formatEncounterList(rows *gorm.DB) (temp []EncounterList, err error) {
|
||||||
@ -169,6 +173,20 @@ func (e *Encounter) ShowByID(id int64) (temp *Encounter, err error) {
|
|||||||
// return
|
// 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,同时去重。
|
* @description: 过去 1 个月,发送过路遇表的 ids,同时去重。
|
||||||
* @param {*} user_id
|
* @param {*} user_id
|
||||||
|
@ -141,17 +141,17 @@ func testInsertDeworming() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func insertData() {
|
func insertData() {
|
||||||
// testInsertSterilzation()
|
testInsertSterilzation()
|
||||||
// fmt.Println("testInsertSterilzation success.")
|
fmt.Println("testInsertSterilzation success.")
|
||||||
|
|
||||||
// testInsertBreed()
|
testInsertBreed()
|
||||||
// fmt.Println("testInsertBreed success.")
|
fmt.Println("testInsertBreed success.")
|
||||||
|
|
||||||
// testInsertStatus()
|
testInsertStatus()
|
||||||
// fmt.Println("testInsertStatus success.")
|
fmt.Println("testInsertStatus success.")
|
||||||
|
|
||||||
// testInsertAnmGender()
|
testInsertAnmGender()
|
||||||
// fmt.Println("testInsertAnmGender success.")
|
fmt.Println("testInsertAnmGender success.")
|
||||||
|
|
||||||
testInsertVaccination()
|
testInsertVaccination()
|
||||||
fmt.Println("testInsertVaccination success.")
|
fmt.Println("testInsertVaccination success.")
|
||||||
@ -163,7 +163,8 @@ func insertData() {
|
|||||||
func main() {
|
func main() {
|
||||||
// 1.
|
// 1.
|
||||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
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)
|
fmt.Println("dsn:", dsn)
|
||||||
dbMySQL, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
dbMySQL, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
225
app/model_es/encounter.go
Normal file
225
app/model_es/encounter.go
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
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 {
|
||||||
|
if encounter == nil { // UPDATE 这样写好丑。
|
||||||
|
return &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
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 改正,仿 Insert
|
||||||
|
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(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, variable.ElasticClient)
|
||||||
|
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
|
||||||
|
}
|
@ -44,8 +44,9 @@ func Upload(context *gin.Context, savePath string) (r bool, finnalSavePath inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 文件上传可以设置按照 xxx年-xx月 格式存储
|
// 文件上传可以设置按照 xxx年-xx月 格式存储
|
||||||
|
// INFO 但这个 returnPath 我还基本没有用到。
|
||||||
func generateYearMonthPath(savePathPre string) (string, string) {
|
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")
|
curYearMonth := time.Now().In(time.Local).Format("2006_01")
|
||||||
newSavePathPre := filepath.Join(savePathPre, curYearMonth)
|
newSavePathPre := filepath.Join(savePathPre, curYearMonth)
|
||||||
newReturnPathPre := filepath.Join(returnPath, curYearMonth)
|
newReturnPathPre := filepath.Join(returnPath, curYearMonth)
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/elastic/go-elasticsearch/v8"
|
||||||
"github.com/yankeguo/zhipu"
|
"github.com/yankeguo/zhipu"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -120,4 +121,13 @@ func init() {
|
|||||||
log.Fatal(my_errors.ErrorsGlmClientInitFail + err.Error())
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ Redis:
|
|||||||
|
|
||||||
Logs:
|
Logs:
|
||||||
GinLogName: "/store/logs/gin.log" #设置 gin 框架的接口访问日志
|
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 表示一般的文本格式
|
TextFormat: "json" #记录日志的格式,参数选项:console、json , console 表示一般的文本格式
|
||||||
TimePrecision: "millisecond" #记录日志时,相关的时间精度,该参数选项:second 、 millisecond , 分别表示 秒 和 毫秒 ,默认为毫秒级别
|
TimePrecision: "millisecond" #记录日志时,相关的时间精度,该参数选项:second 、 millisecond , 分别表示 秒 和 毫秒 ,默认为毫秒级别
|
||||||
MaxSize: 10 #每个日志的最大尺寸(以MB为单位), 超过该值,系统将会自动进行切割
|
MaxSize: 10 #每个日志的最大尺寸(以MB为单位), 超过该值,系统将会自动进行切割
|
||||||
@ -56,11 +56,12 @@ SnowFlake:
|
|||||||
SnowFlakeMachineId: 2 #如果本项目同时部署在多台机器,并且需要同时使用该算法,请为每一台机器设置不同的ID,区间范围: [0,1023]
|
SnowFlakeMachineId: 2 #如果本项目同时部署在多台机器,并且需要同时使用该算法,请为每一台机器设置不同的ID,区间范围: [0,1023]
|
||||||
|
|
||||||
FileUploadSetting:
|
FileUploadSetting:
|
||||||
Size: 32 #设置上传文件的最大值,单位:M,注意: 如果go前置nginx服务器,nginx 默认限制文件上传大小为 50 M ,用户上传文件限制还需要继续修改 nginx 配置
|
Size: 32 # 设置上传文件的最大值,单位:M,注意: 如果go前置nginx服务器,nginx 默认限制文件上传大小为 50 M ,用户上传文件限制还需要继续修改 nginx 配置
|
||||||
UploadFileField: "file" #post上传文件时,表单的键名
|
UploadFileField: "file" # post上传文件时,表单的键名
|
||||||
UploadFileSavePath: "/public/nginx/" #上传文件保存在路径, 该路径与 BasePath 进行拼接使用
|
# TODO 为了方便后续兼容服务器上的 Nginx,直接采取绝对路径,
|
||||||
UploadFileReturnPath: "" # 文件上后返回的路径,由于程序会自动创建软连接,自动将资源定位到实际路径,所有资源的访问入口建议都从public开始
|
UploadFileSavePath: "D:/.File Data/GoProject/catface_back/public/nginx" # 上传文件保存在路径, 该路径与 BasePath 进行拼接使用
|
||||||
AllowMimeType: #允许的文件mime类型列表
|
UploadFileReturnPath: "" # 文件上后返回的路径,由于程序会自动创建软连接,自动将资源定位到实际路径,所有资源的访问入口建议都从public开始
|
||||||
|
AllowMimeType: #允许的文件mime类型列表
|
||||||
- "image/jpeg" #jpg、jpeg图片格式
|
- "image/jpeg" #jpg、jpeg图片格式
|
||||||
- "image/png" #png图片格式
|
- "image/png" #png图片格式
|
||||||
# - "image/x-icon" #ico图片
|
# - "image/x-icon" #ico图片
|
||||||
@ -155,7 +156,14 @@ Glm:
|
|||||||
ApiKey: "0cf510ebc01599dba2a593069c1bdfbc.nQBQ4skP8xBh7ijU"
|
ApiKey: "0cf510ebc01599dba2a593069c1bdfbc.nQBQ4skP8xBh7ijU"
|
||||||
DefaultModel: "glm-4-flash"
|
DefaultModel: "glm-4-flash"
|
||||||
|
|
||||||
|
|
||||||
# qiNiu 云存储配置
|
# qiNiu 云存储配置
|
||||||
QiNiu:
|
QiNiu:
|
||||||
AccessKey: "bI1MpHUBA9OCg4uSJkuJRmScuCJfOlbePe8fCENo"
|
AccessKey: "bI1MpHUBA9OCg4uSJkuJRmScuCJfOlbePe8fCENo"
|
||||||
SecretKey: "UP5-GmSmAYNbMlSb6LYLuKZ-fT35nlEzGvOKKm9S"
|
SecretKey: "UP5-GmSmAYNbMlSb6LYLuKZ-fT35nlEzGvOKKm9S"
|
||||||
|
|
||||||
|
ElasticSearch:
|
||||||
|
Addr: "http://localhost:9200"
|
||||||
|
UserName: "elastic"
|
||||||
|
Password: ""
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ Gormv2: # 只针对 gorm 操作数据库有效
|
|||||||
IsInitGlobalGormMysql: 1 # 随项目启动为gorm db初始化一个全局 variable.GormDbMysql(完全等于*gorm.Db),正确配置数据库,该值必须设置为: 1
|
IsInitGlobalGormMysql: 1 # 随项目启动为gorm db初始化一个全局 variable.GormDbMysql(完全等于*gorm.Db),正确配置数据库,该值必须设置为: 1
|
||||||
SlowThreshold: 30 # 慢 SQL 阈值(sql执行时间超过此时间单位(秒),就会触发系统日志记录)
|
SlowThreshold: 30 # 慢 SQL 阈值(sql执行时间超过此时间单位(秒),就会触发系统日志记录)
|
||||||
Write:
|
Write:
|
||||||
Host: "113.44.68.213"
|
# Host: "113.44.68.213"
|
||||||
|
Host: "127.0.0.1"
|
||||||
DataBase: "hav_cats"
|
DataBase: "hav_cats"
|
||||||
Port: 3306
|
Port: 3306
|
||||||
Prefix: "tb_" # 目前没有用到该配置项
|
Prefix: "tb_" # 目前没有用到该配置项
|
||||||
|
@ -149,6 +149,11 @@ func InitWebRouter() *gin.Engine {
|
|||||||
{
|
{
|
||||||
nlp.POST("title", validatorFactory.Create(consts.ValidatorPrefix+"NlpTitle"))
|
nlp.POST("title", validatorFactory.Create(consts.ValidatorPrefix+"NlpTitle"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
search := backend.Group("search")
|
||||||
|
{
|
||||||
|
search.GET("", validatorFactory.Create(consts.ValidatorPrefix+"SearchAll"))
|
||||||
|
}
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
50
test/es/encounter_test.go
Normal file
50
test/es/encounter_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ func InitElastic() {
|
|||||||
var err error
|
var err error
|
||||||
ElasticClient, err = elasticsearch.NewClient(elasticsearch.Config{
|
ElasticClient, err = elasticsearch.NewClient(elasticsearch.Config{
|
||||||
// Addresses: []string{"http://113.44.68.213:9200"},
|
// 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",
|
// Username: "elastic",
|
||||||
// Password: "U8n61yn*Sp4Kvbuqo_K8",
|
// Password: "U8n61yn*Sp4Kvbuqo_K8",
|
||||||
})
|
})
|
||||||
|
@ -15,7 +15,8 @@ var DB *gorm.DB
|
|||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
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)
|
fmt.Println("dsn:", dsn)
|
||||||
dbMySQL, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
dbMySQL, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user