feat(catface): 优化猫脸识别功能
- 新增猫脸识别失败的错误码和错误信息 - 重构猫脸识别结果处理逻辑,增加对结果为空的处理 - 优化猫脸识别结果展示,包括品种翻译和动物信息展示 - 新增测试用例,验证猫脸识别功能正常工作
This commit is contained in:
parent
d04987302f
commit
609e02dfd2
@ -3,13 +3,19 @@ package errcode
|
|||||||
const (
|
const (
|
||||||
ErrAnimalSqlFind = iota + ErrAnimal
|
ErrAnimalSqlFind = iota + ErrAnimal
|
||||||
AnimalNoFind
|
AnimalNoFind
|
||||||
|
|
||||||
|
// TAG
|
||||||
|
CatFaceFail
|
||||||
|
// CatFaceNoFind // INFO 交给前端判断更合适
|
||||||
)
|
)
|
||||||
|
|
||||||
func AnimalMsgInit(m msg) {
|
func AnimalMsgInit(m msg) {
|
||||||
m[ErrAnimalSqlFind] = "Animals 表单查询失败"
|
m[ErrAnimalSqlFind] = "Animals 表单查询失败"
|
||||||
m[AnimalNoFind] = "Animals 没有查询到符合条件的目标"
|
m[AnimalNoFind] = "Animals 没有查询到符合条件的目标"
|
||||||
|
m[CatFaceFail] = "猫脸识别失败"
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnimalMsgUserInit(m msg) {
|
func AnimalMsgUserInit(m msg) {
|
||||||
m[AnimalNoFind] = "没有更多符合此条件的毛茸茸啦,试着更换查询条件或者新增吧~"
|
m[AnimalNoFind] = "没有更多符合此条件的毛茸茸啦,试着更换查询条件或者新增吧~"
|
||||||
|
m[CatFaceFail] = "猫脸识别失败,请重新尝试~ 😿"
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"catface/app/model"
|
"catface/app/model"
|
||||||
"catface/app/model_es"
|
"catface/app/model_es"
|
||||||
"catface/app/model_redis"
|
"catface/app/model_redis"
|
||||||
|
"catface/app/model_res"
|
||||||
"catface/app/service/animals/curd"
|
"catface/app/service/animals/curd"
|
||||||
"catface/app/service/catface"
|
"catface/app/service/catface"
|
||||||
"catface/app/service/upload_file"
|
"catface/app/service/upload_file"
|
||||||
@ -26,32 +27,52 @@ type Animals struct { // INFO 起到一个标记的作用,这样 web.xxx 的
|
|||||||
func (a *Animals) Guess(context *gin.Context) {
|
func (a *Animals) Guess(context *gin.Context) {
|
||||||
// 1. Get Params
|
// 1. Get Params
|
||||||
filePath := context.GetString(consts.ValidatorPrefix + "file_path")
|
filePath := context.GetString(consts.ValidatorPrefix + "file_path")
|
||||||
// 2. Get Result
|
filePath = filepath.Join(variable.ConfigYml.GetString("FileUploadSetting.UploadFileSavePath"), variable.ConfigYml.GetString("FileUploadSetting.CatFaceTempRootPath"), filePath)
|
||||||
catRes := catface.GetCatfaceResult(filePath)
|
|
||||||
// 3. Response
|
|
||||||
|
|
||||||
type subT struct {
|
// STAGE - 2 Get Result From Python API
|
||||||
Id int64 `json:"id"`
|
catRes, err := catface.GetCatfaceResult(filePath)
|
||||||
Name string `json:"name"`
|
if err != nil {
|
||||||
Status uint8 `json:"status"`
|
response.Fail(context, errcode.CatFaceFail, errcode.ErrMsg[errcode.CatFaceFail], errcode.ErrMsgForUser[errcode.CatFaceFail])
|
||||||
Department uint8 `json:"department"`
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type t struct {
|
// STAGE - 3
|
||||||
List []subT `json:"list"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var resList t
|
// FaceBreed: En -> Zh
|
||||||
for _, v := range catRes.Cats {
|
faceBreedZh := model.CreateAnmBreedFactory("").GetNameZhByEn(catRes.FaceBreed)
|
||||||
resList.List = append(resList.List, subT{
|
|
||||||
Id: v.Id,
|
// CatFace 返回为空,直接返回。
|
||||||
Name: model.CreateAnimalFactory("").ShowByID(v.Id).Name,
|
if len(catRes.Cats) == 0 {
|
||||||
Status: model.CreateAnimalFactory("").ShowByID(v.Id).Status,
|
response.Success(context, consts.CurdStatusOkMsg, gin.H{
|
||||||
Department: model.CreateAnimalFactory("").ShowByID(v.Id).Department,
|
"face_breed": faceBreedZh,
|
||||||
|
"animals": nil,
|
||||||
})
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// fill other information
|
||||||
|
var ids []int64
|
||||||
|
for _, v := range catRes.Cats {
|
||||||
|
ids = append(ids, v.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.Success(context, consts.CurdStatusOkMsg, resList)
|
animals := model.CreateAnimalFactory("").ShowByIDs(ids, "id", "name", "status", "department", "nick_names", "description", "avatar")
|
||||||
|
|
||||||
|
var res []model_res.CatfaceCat
|
||||||
|
for _, animal := range animals {
|
||||||
|
for _, cat := range catRes.Cats {
|
||||||
|
if cat.Id == animal.Id {
|
||||||
|
res = append(res, model_res.CatfaceCat{
|
||||||
|
Animal: animal,
|
||||||
|
Conf: cat.Conf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success(context, consts.CurdStatusOkMsg, gin.H{
|
||||||
|
"face_breed": faceBreedZh,
|
||||||
|
"animals": res,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Animals) List(context *gin.Context) {
|
func (a *Animals) List(context *gin.Context) {
|
||||||
|
@ -5,10 +5,12 @@ import (
|
|||||||
"catface/app/http/controller/web"
|
"catface/app/http/controller/web"
|
||||||
"catface/app/http/validator/core/data_transfer"
|
"catface/app/http/validator/core/data_transfer"
|
||||||
"catface/app/utils/response"
|
"catface/app/utils/response"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CatfaceGuess struct {
|
type CatfaceGuess struct {
|
||||||
|
FilePath string `form:"file_path" json:"file_path" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CatfaceGuess) CheckParams(context *gin.Context) {
|
func (c CatfaceGuess) CheckParams(context *gin.Context) {
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
// INFO 一些基础表单的整合
|
// TAG 一些基础表单的整合
|
||||||
|
|
||||||
type AnmBreed struct {
|
|
||||||
BriefModel
|
|
||||||
}
|
|
||||||
|
|
||||||
type AnmSterilzation struct { // TEST How to use BriefModel, the dif between Common
|
type AnmSterilzation struct { // TEST How to use BriefModel, the dif between Common
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
NameZh string `json:"name_zh"`
|
NameZh string `json:"name_zh"`
|
||||||
@ -28,3 +23,24 @@ type AnmVaccination struct {
|
|||||||
type AnmDeworming struct {
|
type AnmDeworming struct {
|
||||||
BriefModel
|
BriefModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TAG 带了点函数处理
|
||||||
|
func CreateAnmBreedFactory(sqlType string) *AnmBreed {
|
||||||
|
return &AnmBreed{BriefModel: BriefModel{DB: UseDbConn(sqlType)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnmBreed struct {
|
||||||
|
BriefModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AnmBreed) TableName() string {
|
||||||
|
return "anm_breeds"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AnmBreed) GetNameZhByEn(name_en string) string {
|
||||||
|
var temp AnmBreed
|
||||||
|
if result := a.DB.Where("name_en = ?", name_en).First(&temp); result.Error != nil {
|
||||||
|
return "" // 如果查询失败,返回空字符串
|
||||||
|
}
|
||||||
|
return temp.NameZh
|
||||||
|
}
|
||||||
|
@ -20,9 +20,10 @@ type BaseModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BriefModel struct {
|
type BriefModel struct {
|
||||||
Id int64 `json:"id"`
|
*gorm.DB `gorm:"-" json:"-"`
|
||||||
NameZh string `json:"name_zh"`
|
Id int64 `json:"id"`
|
||||||
NameEn string `json:"name_en"`
|
NameZh string `json:"name_zh"`
|
||||||
|
NameEn string `json:"name_en"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Color struct {
|
type Color struct {
|
||||||
|
8
app/model_res/catface.go
Normal file
8
app/model_res/catface.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package model_res
|
||||||
|
|
||||||
|
import "catface/app/model"
|
||||||
|
|
||||||
|
type CatfaceCat struct {
|
||||||
|
Animal model.Animal `json:"animal"`
|
||||||
|
Conf float64 `json:"conf"`
|
||||||
|
}
|
@ -4,29 +4,42 @@ import (
|
|||||||
"catface/app/global/variable"
|
"catface/app/global/variable"
|
||||||
"catface/app/utils/micro_service"
|
"catface/app/utils/micro_service"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/carlmjohnson/requests"
|
"github.com/carlmjohnson/requests"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FaceRes struct {
|
type FaceData struct {
|
||||||
FaceBreed int `json:"face_breed"`
|
FaceBreed string `json:"face_breed"`
|
||||||
Cats []struct {
|
Cats []struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Prob float64 `json:"prob"`
|
Conf float64 `json:"conf"`
|
||||||
} `json:"cats"`
|
} `json:"cats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCatfaceResult(filePath string) FaceRes {
|
type CatFaceRes struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Data FaceData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCatfaceResult(filePath string) (*FaceData, error) {
|
||||||
body := map[string]interface{}{
|
body := map[string]interface{}{
|
||||||
"file_path": filePath,
|
"file_path": filePath,
|
||||||
}
|
}
|
||||||
var res FaceRes
|
var res CatFaceRes
|
||||||
err := requests.URL(micro_service.FetchPythonServiceUrl("cnn/detect_cat")).
|
err := requests.URL(micro_service.FetchPythonServiceUrl("cnn/detect_cat")).
|
||||||
BodyJSON(&body).
|
BodyJSON(&body).
|
||||||
ToJSON(&res).
|
ToJSON(&res).
|
||||||
Fetch(context.Background())
|
Fetch(context.Background())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
variable.ZapLog.Error("获取cat face结果集失败: " + err.Error())
|
variable.ZapLog.Error("获取cat face结果集失败: " + err.Error())
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
if res.Status != 200 {
|
||||||
|
return nil, fmt.Errorf("请求状态码错误: %d", res.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res.Data, nil
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ FileUploadSetting:
|
|||||||
|
|
||||||
AvatarWidth: 200
|
AvatarWidth: 200
|
||||||
DocsRootPath: "docs" # TODO 或许 upload 模块可以写一下自动区分大致的文件类型所在的位置。
|
DocsRootPath: "docs" # TODO 或许 upload 模块可以写一下自动区分大致的文件类型所在的位置。
|
||||||
|
CatFaceTempRootPath: "catfaceTemp"
|
||||||
|
|
||||||
# casbin 权限控制api接口
|
# casbin 权限控制api接口
|
||||||
Casbin:
|
Casbin:
|
||||||
|
@ -42,5 +42,5 @@ func TestExplain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 调用 StructToString 函数
|
// 调用 StructToString 函数
|
||||||
t.Logf("结构体内容:", StructToString(encounter))
|
t.Logf("结构体内容:%v", StructToString(encounter))
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"catface/app/model"
|
"catface/app/model"
|
||||||
|
_ "catface/bootstrap"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,3 +15,8 @@ func TestDocModel(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnmBreed(t *testing.T) {
|
||||||
|
res := model.CreateAnmBreedFactory("").GetNameZhByEn("li")
|
||||||
|
t.Log(res)
|
||||||
|
}
|
||||||
|
@ -1,97 +1,98 @@
|
|||||||
//后端代码
|
// //后端代码
|
||||||
|
|
||||||
//注意 **我注释的代码,是不使用gin框架封装的Stream方法,也就是C.Stream(func())和C.ssevent(),只是C.Stream要改成for循环持续的从通道里面进行读,直到通道关闭,结束for循环**
|
// //注意 **我注释的代码,是不使用gin框架封装的Stream方法,也就是C.Stream(func())和C.ssevent(),只是C.Stream要改成for循环持续的从通道里面进行读,直到通道关闭,结束for循环**
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"catface/app/service/nlp/glm"
|
// "catface/app/service/nlp/glm"
|
||||||
_ "catface/bootstrap"
|
// _ "catface/bootstrap"
|
||||||
"fmt"
|
// "fmt"
|
||||||
"io"
|
// "io"
|
||||||
"testing"
|
// "testing"
|
||||||
// "time"
|
// // "time"
|
||||||
|
//
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
// "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
)
|
// )
|
||||||
|
|
||||||
func SSE(c *gin.Context) {
|
// func SSE(c *gin.Context) {
|
||||||
// 设置响应头,告诉前端适用event-stream事件流交互
|
// // 设置响应头,告诉前端适用event-stream事件流交互
|
||||||
//c.Writer.Header().Set("Content-Type", "text/event-stream")
|
// //c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||||
//c.Writer.Header().Set("Cache-Control", "no-cache")
|
// //c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||||
//c.Writer.Header().Set("Connection", "keep-alive")
|
// //c.Writer.Header().Set("Connection", "keep-alive")
|
||||||
|
|
||||||
// 判断是否支持sse
|
// // 判断是否支持sse
|
||||||
//w := c.Writer
|
// //w := c.Writer
|
||||||
//flusher, _ := w.(http.Flusher)
|
// //flusher, _ := w.(http.Flusher)
|
||||||
query := c.Query("query")
|
// query := c.Query("query")
|
||||||
|
|
||||||
// 接收前端页面关闭连接通知
|
// // 接收前端页面关闭连接通知
|
||||||
closeNotify := c.Request.Context().Done()
|
// closeNotify := c.Request.Context().Done()
|
||||||
|
|
||||||
// 开启协程监听前端页面是否关闭了连接,关闭连接会触发此方法
|
// // 开启协程监听前端页面是否关闭了连接,关闭连接会触发此方法
|
||||||
go func() {
|
// go func() {
|
||||||
<-closeNotify
|
// <-closeNotify
|
||||||
fmt.Println("SSE关闭了")
|
// fmt.Println("SSE关闭了")
|
||||||
return
|
// return
|
||||||
}()
|
// }()
|
||||||
|
|
||||||
//新建一个通道,用于数据接收和响应
|
// //新建一个通道,用于数据接收和响应
|
||||||
Chan := make(chan string)
|
// Chan := make(chan string)
|
||||||
|
|
||||||
// 异步接收GPT响应,然后把响应的数据发送到通道Chan
|
// // 异步接收GPT响应,然后把响应的数据发送到通道Chan
|
||||||
go func() {
|
// go func() {
|
||||||
err := glm.ChatStream(query, Chan)
|
// err := glm.ChatStream(query, Chan)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Println("Error", err)
|
// fmt.Println("Error", err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
close(Chan)
|
// close(Chan)
|
||||||
}()
|
// }()
|
||||||
|
|
||||||
// gin框架封装的stream,会持续的调用这个func方法,记得返回true;返回false代表结束调用func方法
|
// // gin框架封装的stream,会持续的调用这个func方法,记得返回true;返回false代表结束调用func方法
|
||||||
c.Stream(func(w io.Writer) bool {
|
// c.Stream(func(w io.Writer) bool {
|
||||||
select {
|
// select {
|
||||||
case i, ok := <-Chan:
|
// case i, ok := <-Chan:
|
||||||
if !ok {
|
// if !ok {
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
c.SSEvent("chat", i) // c.SSEvent会自动修改响应头为事件流,并发送”test“事件流给前端监听”test“的回调方法
|
// c.SSEvent("chat", i) // c.SSEvent会自动修改响应头为事件流,并发送”test“事件流给前端监听”test“的回调方法
|
||||||
//flusher.Flush() // 确保立即发送
|
// //flusher.Flush() // 确保立即发送
|
||||||
return true
|
// return true
|
||||||
case <-closeNotify:
|
// case <-closeNotify:
|
||||||
fmt.Println("SSE关闭了")
|
// fmt.Println("SSE关闭了")
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestSSE(t *testing.T) {
|
// func TestSSE(t *testing.T) {
|
||||||
engine := gin.Default()
|
// engine := gin.Default()
|
||||||
// 设置跨域中间件
|
// // 设置跨域中间件
|
||||||
engine.Use(func(context *gin.Context) {
|
// engine.Use(func(context *gin.Context) {
|
||||||
origin := context.GetHeader("Origin")
|
// origin := context.GetHeader("Origin")
|
||||||
// 允许 Origin 字段中的域发送请求
|
// // 允许 Origin 字段中的域发送请求
|
||||||
context.Writer.Header().Add("Access-Control-Allow-Origin", origin) // 这边我的前端页面在63342,会涉及跨域,这个根据自己情况设置,或者直接设置为”*“,放行所有的
|
// context.Writer.Header().Add("Access-Control-Allow-Origin", origin) // 这边我的前端页面在63342,会涉及跨域,这个根据自己情况设置,或者直接设置为”*“,放行所有的
|
||||||
// 设置预验请求有效期为 86400 秒
|
// // 设置预验请求有效期为 86400 秒
|
||||||
context.Writer.Header().Set("Access-Control-Max-Age", "86400")
|
// context.Writer.Header().Set("Access-Control-Max-Age", "86400")
|
||||||
// 设置允许请求的方法
|
// // 设置允许请求的方法
|
||||||
context.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, U`PDATE, PATCH")
|
// context.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, U`PDATE, PATCH")
|
||||||
// 设置允许请求的 Header
|
// // 设置允许请求的 Header
|
||||||
context.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Apitoken")
|
// context.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Apitoken")
|
||||||
// 设置拿到除基本字段外的其他字段,如上面的Apitoken, 这里通过引用Access-Control-Expose-Headers,进行配置,效果是一样的。
|
// // 设置拿到除基本字段外的其他字段,如上面的Apitoken, 这里通过引用Access-Control-Expose-Headers,进行配置,效果是一样的。
|
||||||
context.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Headers")
|
// context.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Headers")
|
||||||
// 配置是否可以带认证信息
|
// // 配置是否可以带认证信息
|
||||||
context.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
// context.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
// OPTIONS请求返回200
|
// // OPTIONS请求返回200
|
||||||
if context.Request.Method == "OPTIONS" {
|
// if context.Request.Method == "OPTIONS" {
|
||||||
fmt.Println(context.Request.Header)
|
// fmt.Println(context.Request.Header)
|
||||||
context.AbortWithStatus(200)
|
// context.AbortWithStatus(200)
|
||||||
} else {
|
// } else {
|
||||||
context.Next()
|
// context.Next()
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
engine.GET("/admin/rag/default_talk", SSE) // TIP 记得适用get请求,我用post前端报404,资料说是SSE只支持get请求
|
// engine.GET("/admin/rag/default_talk", SSE) // TIP 记得适用get请求,我用post前端报404,资料说是SSE只支持get请求
|
||||||
engine.Run(":20201")
|
// engine.Run(":20201")
|
||||||
}
|
// }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user