Toggle navigation
Toggle navigation
This project
Loading...
Sign in
lizhengwei
/
aigc-embedding-service
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
wangdongxu
2026-04-03 11:20:49 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
6fee28d24f44e9c88a9719bb96ed9e27a210ba3f
6fee28d2
1 parent
4fc18be4
jira:NYJ-1460 desc:接口代码
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
156 additions
and
20 deletions
Dockerfile
api/data_router.py
api/http_log.py
api/router.py
config.py
main.py
Dockerfile
View file @
6fee28d
# 应用镜像 - 基于基础镜像部署代码
FROM aigc-embedding-service-base:latest
ENV PROJECT_ROOT=/app ENV=docker
# 设置工作目录
WORKDIR /app
# 复制应用代码(包含配置文件)
COPY app/ ./app/
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["python", "main.py"]
...
...
api/data_router.py
0 → 100644
View file @
6fee28d
from
typing
import
List
,
Optional
,
Literal
,
Union
,
Dict
,
Any
,
Annotated
from
pydantic
import
BaseModel
,
Field
from
fastapi
import
APIRouter
from
api.resp_bean
import
RespBean
,
success
router
=
APIRouter
()
# ============== 请求/响应模型定义 ==============
class
FaceKwargs
(
BaseModel
):
"""人脸类型扩展字段"""
person_id
:
Optional
[
str
]
=
Field
(
None
,
description
=
"人物id"
)
person_name
:
Optional
[
str
]
=
Field
(
None
,
description
=
"人物姓名"
)
class
SportShotKwargs
(
BaseModel
):
"""体育镜头类型扩展字段"""
match_name
:
Optional
[
str
]
=
Field
(
None
,
description
=
"比赛名称"
)
person_name
:
Optional
[
str
]
=
Field
(
None
,
description
=
"人名"
)
video_desc
:
Optional
[
str
]
=
Field
(
None
,
description
=
"视频描述"
)
shot_cls
:
Optional
[
str
]
=
Field
(
None
,
description
=
"镜头分类"
)
match_id
:
Optional
[
str
]
=
Field
(
None
,
description
=
"比赛id"
)
program_id
:
Optional
[
str
]
=
Field
(
None
,
description
=
"节目id"
)
# 定义类型映射,用于根据type自动转换kwargs
KwargsType
=
Union
[
FaceKwargs
,
SportShotKwargs
]
class
PutDataRequest
(
BaseModel
):
"""写入数据请求体"""
id
:
str
=
Field
(
...
,
description
=
"唯一键id"
)
embedding
:
List
[
float
]
=
Field
(
...
,
description
=
"向量"
)
embedding_version
:
str
=
Field
(
...
,
description
=
"向量版本 小写数字下划线组成"
)
kwargs
:
Optional
[
Dict
[
str
,
Any
]]
=
Field
(
None
,
description
=
"扩展字段,根据type不同字段不同"
)
def
get_typed_kwargs
(
self
,
type
:
str
)
->
Optional
[
KwargsType
]:
"""根据type自动转换kwargs为具体类型"""
if
self
.
kwargs
is
None
:
return
None
type_map
=
{
'face'
:
FaceKwargs
,
'sport_shot'
:
SportShotKwargs
}
kwargs_class
=
type_map
.
get
(
type
)
if
kwargs_class
:
return
kwargs_class
(
**
self
.
kwargs
)
return
None
class
DelByIdRequest
(
BaseModel
):
"""删除数据请求体"""
ids
:
List
[
str
]
=
Field
(
...
,
description
=
"id 集合"
)
class
FilterItem
(
BaseModel
):
"""过滤条件项"""
name
:
str
=
Field
(
...
,
description
=
"字段名"
)
value
:
List
=
Field
(
...
,
description
=
"字段值"
)
opt
:
Optional
[
Literal
[
"eq"
,
"neq"
,
"lt"
,
"gt"
,
"lte"
,
"gte"
,
"like"
,
"in"
]]
=
Field
(
None
,
description
=
"操作类型: eq相等, neq不等, lt小于, gt大于, lte小于等于, gte大于等于, like模糊匹配, in内"
)
class
SearchRequest
(
BaseModel
):
"""检索数据请求体"""
embedding
:
List
[
float
]
=
Field
(
...
,
description
=
"向量"
)
embedding_version
:
str
=
Field
(
...
,
description
=
"向量版本 小写数字下划线组成"
)
topk
:
int
=
Field
(
...
,
description
=
"top k"
)
filters
:
Optional
[
List
[
FilterItem
]]
=
Field
(
None
,
description
=
"过滤字段"
)
class
SearchResultItem
(
BaseModel
):
"""检索结果单项"""
id
:
str
=
Field
(
...
,
description
=
"id"
)
kwargs
:
dict
=
Field
(
...
,
description
=
"扩展字段"
)
score
:
float
=
Field
(
...
,
description
=
"分值"
)
class
SearchResponseData
(
BaseModel
):
"""检索响应数据"""
type
:
str
=
Field
(
...
,
description
=
"type"
)
data_list
:
List
[
SearchResultItem
]
=
Field
(
...
,
description
=
"topk结果列表"
)
# ============== 接口定义 ==============
@router.put
(
"/{type}/put"
,
response_model
=
RespBean
,
tags
=
[
"数据服务"
],
summary
=
"写入数据"
)
@router.post
(
"/{type}/put"
,
response_model
=
RespBean
,
tags
=
[
"数据服务"
],
summary
=
"写入数据"
)
async
def
put_data
(
type
:
Literal
[
"face"
,
"sport_shot"
],
request
:
PutDataRequest
):
"""
写入数据
"""
pass
return
success
()
@router.delete
(
"/{type}/del_by_id"
,
response_model
=
RespBean
,
tags
=
[
"数据服务"
],
summary
=
"删除数据"
)
@router.post
(
"/{type}/del_by_id"
,
response_model
=
RespBean
,
tags
=
[
"数据服务"
],
summary
=
"删除数据"
)
async
def
del_by_id
(
type
:
Literal
[
"face"
,
"sport_shot"
],
request
:
DelByIdRequest
):
"""
删除数据
- **type**: 数据类型 (face-人脸, sport_shot-体育镜头)
- **ids**: id集合
"""
# TODO: 实现数据删除逻辑
pass
return
success
()
@router.post
(
"/{type}/search"
,
response_model
=
RespBean
[
SearchResponseData
],
tags
=
[
"数据服务"
],
summary
=
"检索数据"
)
async
def
search_data
(
type
:
Literal
[
"face"
,
"sport_shot"
],
request
:
SearchRequest
):
"""
检索数据
- **type**: 数据类型 (face-人脸, sport_shot-体育镜头)
- **embedding**: 查询向量
- **embedding_version**: 向量版本
- **topk**: 返回top k条结果
- **filters**: 过滤条件列表
"""
# TODO: 实现数据检索逻辑
pass
return
success
()
...
...
api/http_log.py
View file @
6fee28d
...
...
@@ -11,7 +11,6 @@ from config import settings
class
LoggingMiddleware
(
BaseHTTPMiddleware
):
# 日志记录路径前缀
async
def
dispatch
(
self
,
request
:
Request
,
call_next
):
# 获取请求信息
method
=
request
.
method
...
...
@@ -40,11 +39,6 @@ class LoggingMiddleware(BaseHTTPMiddleware):
except
Exception
:
pass
# 记录请求日志
logger
.
info
(
f
"→ HTTP Request | {method} {url} | Client: {client_host}"
)
if
request_body
:
logger
.
info
(
f
" Request Body: {request_body}"
)
# 处理请求
response
=
await
call_next
(
request
)
...
...
@@ -77,11 +71,22 @@ class LoggingMiddleware(BaseHTTPMiddleware):
except
Exception
:
pass
#
记录响应日志
#
构建单条日志记录
status_code
=
response
.
status_code
logger
.
info
(
f
"← HTTP Response | {method} {url} | Status: {status_code} | Time: {process_time:.3f}s"
)
log_data
=
{
"method"
:
method
,
"url"
:
url
,
"client"
:
client_host
,
"status"
:
status_code
,
"time"
:
f
"{process_time:.3f}s"
}
if
request_body
:
log_data
[
"request_body"
]
=
request_body
if
response_body
:
logger
.
info
(
f
" Response Body: {response_body}"
)
log_data
[
"response_body"
]
=
response_body
logger
.
info
(
json
.
dumps
(
log_data
,
ensure_ascii
=
False
))
return
response
else
:
...
...
api/router.py
View file @
6fee28d
from
fastapi
import
APIRouter
from
config
import
settings
from
api.data_router
import
router
as
data_router
api_router
=
APIRouter
(
prefix
=
settings
.
url_prefix
)
def
register_router
(
router
:
APIRouter
,
prefix
:
str
):
api_router
.
include_router
(
router
,
prefix
=
prefix
)
# 注册数据路由
register_router
(
data_router
,
prefix
=
"/data"
)
...
...
config.py
View file @
6fee28d
from
aabd.base.cfg_loader
import
load_yaml_by_file_with_env
from
aabd.base.enhance_dict
import
EnhanceDict
,
read_prefixed_env_vars
from
pathlib
import
Path
from
logging
import
getLogger
logger
=
getLogger
(
__name__
)
cfg_dir
=
Path
(
__file__
)
.
parent
.
absolute
()
/
'cfg'
settings
=
EnhanceDict
(
load_yaml_by_file_with_env
((
cfg_dir
/
'config.yaml'
)
.
as_posix
()))
settings
.
update
(
read_prefixed_env_vars
(
'APP_'
))
settings
.
update
(
read_prefixed_env_vars
(
'EMB_'
))
logger
.
info
(
f
'Settings: {settings}'
)
\ No newline at end of file
...
...
main.py
View file @
6fee28d
...
...
@@ -50,8 +50,9 @@ config_fastapi(app)
if
__name__
==
"__main__"
:
import
uvicorn
logger
.
info
(
f
'docs_url: http://127.0.0.1:{settings.port}/docs'
)
uvicorn
.
run
(
"main:app"
,
app
,
host
=
'0.0.0.0'
,
port
=
settings
.
port
,
reload
=
settings
.
debug
,
...
...
Please
register
or
login
to post a comment