2025-02-15 23:36:20 +08:00
|
|
|
|
# _*_ coding : UTF-8 _*_
|
|
|
|
|
# @Time : 2025/02/13 19:20
|
|
|
|
|
# @UpdateTime : 2025/02/13 19:20
|
|
|
|
|
# @Author : sonder
|
|
|
|
|
# @File : code.py
|
|
|
|
|
# @Software : PyCharm
|
|
|
|
|
# @Comment : 本程序
|
|
|
|
|
import os
|
|
|
|
|
import time
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
from elasticsearch.helpers import async_bulk
|
|
|
|
|
from fastapi import APIRouter, Depends, Path, Request, Query
|
|
|
|
|
from fastapi.encoders import jsonable_encoder
|
|
|
|
|
from fastapi.responses import JSONResponse, FileResponse
|
|
|
|
|
|
2025-02-24 18:33:22 +08:00
|
|
|
|
from annotation.auth import Auth, hasAuth
|
2025-02-15 23:36:20 +08:00
|
|
|
|
from annotation.log import Log
|
|
|
|
|
from config.constant import BusinessType
|
|
|
|
|
from config.env import ElasticSearchConfig
|
|
|
|
|
from controller.login import LoginController
|
|
|
|
|
from exceptions.exception import ServiceException, PermissionException
|
|
|
|
|
from models import File, Code, QueryCode, QueryCodeLog
|
2025-02-23 23:50:42 +08:00
|
|
|
|
from schemas.code import GetCodeInfoResponse, GetCodeListResponse, GetQueryCodeParams, QueryCodeResponse, AddCodeParams, \
|
|
|
|
|
GetQueryCodeLogResponse, GetQueryCodeLogDetailResponse, \
|
2025-02-18 02:04:40 +08:00
|
|
|
|
GetCodeLogAllResponse
|
2025-02-23 23:50:42 +08:00
|
|
|
|
from schemas.common import BaseResponse, DeleteListParams
|
2025-02-15 23:36:20 +08:00
|
|
|
|
from utils.log import logger
|
|
|
|
|
from utils.response import Response
|
|
|
|
|
|
|
|
|
|
codeAPI = APIRouter(
|
2025-02-23 23:50:42 +08:00
|
|
|
|
prefix="/code"
|
2025-02-15 23:36:20 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/template", summary="获取上传编码模板")
|
|
|
|
|
@Log(title="获取上传编码模板", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:uploadTemplate"])
|
|
|
|
|
async def get_upload_template(request: Request, current_user=Depends(LoginController.get_current_user)):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
template_path = os.path.join(os.path.abspath(os.getcwd()), 'assets', 'templates', '上传模版.xlsx')
|
|
|
|
|
if not os.path.exists(template_path):
|
|
|
|
|
raise ServiceException(message="文件不存在!")
|
|
|
|
|
return FileResponse(
|
|
|
|
|
path=template_path,
|
|
|
|
|
filename="上传模版.xlsx",
|
|
|
|
|
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/queryTemplate", summary="获取查询编码模板")
|
|
|
|
|
@Log(title="获取查询编码模板", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:queryTemplate"])
|
|
|
|
|
async def get_query_template(request: Request, current_user=Depends(LoginController.get_current_user)):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
template_path = os.path.join(os.path.abspath(os.getcwd()), 'assets', 'templates', '查询模版.xlsx')
|
|
|
|
|
if not os.path.exists(template_path):
|
|
|
|
|
raise ServiceException(message="文件不存在!")
|
|
|
|
|
return FileResponse(
|
|
|
|
|
path=template_path,
|
|
|
|
|
filename="查询模版.xlsx",
|
|
|
|
|
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.post("/add", response_class=JSONResponse, response_model=BaseResponse, summary="添加编码")
|
|
|
|
|
@Log(title="添加编码", business_type=BusinessType.INSERT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:add"])
|
|
|
|
|
async def add_code(request: Request, params: AddCodeParams, current_user=Depends(LoginController.get_current_user)):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
params.code = params.code.replace(".", "").replace("/", "").replace("_", "").replace("-", "").replace("?",
|
|
|
|
|
"").replace(
|
|
|
|
|
":", "").replace(":", "").replace("?", "").strip()
|
2025-02-23 23:50:42 +08:00
|
|
|
|
if await Code.get_or_none(code=params.code, del_flag=1):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
return Response.failure(msg="编码已存在")
|
|
|
|
|
else:
|
|
|
|
|
if await request.app.state.es.indices.exists(index=ElasticSearchConfig.ES_INDEX):
|
|
|
|
|
await request.app.state.es.indices.create(index=ElasticSearchConfig.ES_INDEX, ignore=400)
|
|
|
|
|
code = await Code.create(
|
|
|
|
|
code=params.code,
|
|
|
|
|
description=params.description
|
|
|
|
|
)
|
|
|
|
|
if code:
|
|
|
|
|
# 构造 Bulk 导入数据
|
|
|
|
|
actions = [
|
|
|
|
|
{
|
|
|
|
|
"_index": ElasticSearchConfig.ES_INDEX,
|
|
|
|
|
"_id": code.code, # 以 code 作为 ID
|
|
|
|
|
"_source": {
|
|
|
|
|
"code": code.code,
|
|
|
|
|
"description": code.description
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
success, failed = await async_bulk(request.app.state.es, actions)
|
|
|
|
|
logger.info(f"成功导入 {success} 条数据,失败 {failed} 条")
|
|
|
|
|
return Response.success(msg="添加成功")
|
|
|
|
|
else:
|
|
|
|
|
return Response.failure(msg="添加失败")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/addCode/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="导入编码")
|
|
|
|
|
@Log(title="导入编码", business_type=BusinessType.INSERT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:import"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def add_code_by_file(request: Request, id: str = Path(description="文件ID"),
|
|
|
|
|
current_user=Depends(LoginController.get_current_user)):
|
|
|
|
|
user_id = current_user.get("id")
|
2025-02-23 23:50:42 +08:00
|
|
|
|
if file := await File.get_or_none(id=id, del_flag=1):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
uploader_id = await file.first().values(id="uploader__id")
|
|
|
|
|
if str(uploader_id["id"]) == user_id:
|
|
|
|
|
try:
|
2025-02-19 02:28:49 +08:00
|
|
|
|
df = pd.read_excel(file.absolute_path, dtype={"code": str})
|
|
|
|
|
df["code"] = df["code"].astype(str).str.zfill(8)
|
2025-02-15 23:36:20 +08:00
|
|
|
|
for index, row in df.iterrows():
|
2025-02-19 02:28:49 +08:00
|
|
|
|
row["code"] = row["code"].replace(".", "").replace("/", "").replace("_", "").replace("-",
|
2025-02-23 23:50:42 +08:00
|
|
|
|
"").replace(
|
2025-02-15 23:36:20 +08:00
|
|
|
|
"?",
|
|
|
|
|
"").replace(
|
|
|
|
|
":", "").replace(":", "").replace("?", "").strip()
|
|
|
|
|
await Code.create(
|
|
|
|
|
code=row["code"],
|
|
|
|
|
description=row["description"]
|
|
|
|
|
)
|
|
|
|
|
if not await request.app.state.es.indices.exists(index=ElasticSearchConfig.ES_INDEX):
|
|
|
|
|
await request.app.state.es.indices.create(index=ElasticSearchConfig.ES_INDEX, ignore=400)
|
|
|
|
|
# 构造 Bulk 导入数据
|
|
|
|
|
actions = [
|
|
|
|
|
{
|
|
|
|
|
"_index": ElasticSearchConfig.ES_INDEX,
|
|
|
|
|
"_id": row["code"], # 以 code 作为 ID
|
|
|
|
|
"_source": {
|
|
|
|
|
"code": row["code"],
|
|
|
|
|
"description": row["description"]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, row in df.iterrows()
|
|
|
|
|
]
|
|
|
|
|
success, failed = await async_bulk(request.app.state.es, actions)
|
|
|
|
|
logger.info(f"成功导入 {success} 条数据,失败 {failed} 条")
|
|
|
|
|
except ServiceException as e:
|
|
|
|
|
logger.error(e.message)
|
|
|
|
|
raise ServiceException(message="文件读取失败")
|
|
|
|
|
return Response.success(msg="添加成功")
|
|
|
|
|
else:
|
|
|
|
|
raise PermissionException(message="权限不足")
|
|
|
|
|
else:
|
|
|
|
|
return Response.failure(msg="文件不存在")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.delete("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除编码")
|
|
|
|
|
@codeAPI.post("/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除编码")
|
|
|
|
|
@Log(title="删除编码", business_type=BusinessType.DELETE)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:delete"])
|
|
|
|
|
async def delete_code_by_id(request: Request, id: str = Path(description="编码ID"),
|
|
|
|
|
current_user=Depends(LoginController.get_current_user)):
|
|
|
|
|
if code := await Code.get_or_none(id=id, del_flag=1):
|
|
|
|
|
code.del_flag = 0
|
|
|
|
|
await code.save()
|
2025-02-15 23:36:20 +08:00
|
|
|
|
await request.app.state.es.delete(index=ElasticSearchConfig.ES_INDEX, id=code.code)
|
|
|
|
|
return Response.success(msg="删除成功")
|
|
|
|
|
else:
|
|
|
|
|
return Response.failure(msg="编码不存在")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.delete("/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除编码")
|
|
|
|
|
@codeAPI.post("/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除编码")
|
|
|
|
|
@Log(title="批量删除编码", business_type=BusinessType.DELETE)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:delete"])
|
|
|
|
|
async def delete_code_by_ids(request: Request, params: DeleteListParams,
|
|
|
|
|
current_user=Depends(LoginController.get_current_user)):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
for id in set(params.ids):
|
2025-02-23 23:50:42 +08:00
|
|
|
|
if code := await Code.get_or_none(id=id, del_flag=1):
|
|
|
|
|
code.del_flag = 0
|
|
|
|
|
await code.save()
|
2025-02-15 23:36:20 +08:00
|
|
|
|
await request.app.state.es.delete(index=ElasticSearchConfig.ES_INDEX, id=code.code)
|
|
|
|
|
return Response.success(msg="删除成功")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.put("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新编码")
|
|
|
|
|
@codeAPI.post("/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="更新编码")
|
|
|
|
|
@Log(title="更新编码", business_type=BusinessType.UPDATE)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:update"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def update_code(request: Request,
|
|
|
|
|
params: AddCodeParams,
|
2025-02-23 23:50:42 +08:00
|
|
|
|
id: str = Path(description="编码ID"),
|
|
|
|
|
current_user=Depends(LoginController.get_current_user)):
|
|
|
|
|
if code := await Code.get_or_none(id=id, del_flag=1):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
code.code = params.code
|
|
|
|
|
code.description = params.description
|
|
|
|
|
await code.save()
|
|
|
|
|
await request.app.state.es.update(index=ElasticSearchConfig.ES_INDEX, id=code.code,
|
|
|
|
|
body={"doc": {"description": params.description}})
|
|
|
|
|
return Response.success(msg="更新成功")
|
|
|
|
|
return Response.failure(msg="编码不存在")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/info/{id}", response_class=JSONResponse, response_model=GetCodeInfoResponse, summary="获取编码信息")
|
|
|
|
|
@Log(title="获取编码信息", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:info"])
|
|
|
|
|
async def get_code_info(request: Request, id: str = Path(description="编码ID"),
|
|
|
|
|
current_user=Depends(LoginController.get_current_user)):
|
|
|
|
|
if code := await Code.get_or_none(id=id, del_flag=1).values(
|
2025-02-15 23:36:20 +08:00
|
|
|
|
id="id",
|
|
|
|
|
code="code",
|
|
|
|
|
description="description",
|
|
|
|
|
create_time="create_time",
|
|
|
|
|
create_by="create_by",
|
|
|
|
|
update_time="update_time",
|
|
|
|
|
update_by="update_by"
|
|
|
|
|
):
|
|
|
|
|
return Response.success(data=code)
|
|
|
|
|
return Response.failure(msg="编码不存在")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/list", response_class=JSONResponse, response_model=GetCodeListResponse, summary="获取编码列表")
|
|
|
|
|
@Log(title="获取编码列表", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:list"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def get_code_list(request: Request,
|
|
|
|
|
page: int = Query(default=1, description="页码"),
|
|
|
|
|
pageSize: int = Query(default=10, description="每页数量"),
|
|
|
|
|
code: Optional[str] = Query(default=None, description="编码"),
|
2025-02-23 23:50:42 +08:00
|
|
|
|
description: Optional[str] = Query(default=None, description="商品描述"),
|
|
|
|
|
current_user=Depends(LoginController.get_current_user)):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
filterArgs = {
|
|
|
|
|
f'{k}__contains': v for k, v in {
|
|
|
|
|
'code': code,
|
|
|
|
|
'description': description
|
|
|
|
|
}.items() if v
|
|
|
|
|
}
|
2025-02-23 23:50:42 +08:00
|
|
|
|
total = await Code.filter(**filterArgs, del_flag=1).count()
|
|
|
|
|
data = await Code.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values(
|
2025-02-15 23:36:20 +08:00
|
|
|
|
id="id",
|
|
|
|
|
code="code",
|
|
|
|
|
description="description",
|
|
|
|
|
create_time="create_time",
|
|
|
|
|
create_by="create_by",
|
|
|
|
|
update_time="update_time",
|
|
|
|
|
update_by="update_by"
|
|
|
|
|
)
|
|
|
|
|
return Response.success(data={
|
|
|
|
|
"page": page,
|
|
|
|
|
"pageSize": pageSize,
|
|
|
|
|
"total": total,
|
|
|
|
|
"result": data
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.post("/query", response_class=JSONResponse, response_model=QueryCodeResponse, summary="查询编码")
|
|
|
|
|
@Log(title="查询编码", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:query"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def get_code_list(request: Request,
|
|
|
|
|
params: GetQueryCodeParams,
|
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user),
|
|
|
|
|
):
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
user_id = current_user.get("id")
|
|
|
|
|
if log := await QueryCodeLog.create(
|
|
|
|
|
operator_id=user_id,
|
|
|
|
|
query_count=0,
|
|
|
|
|
result_count=0,
|
|
|
|
|
cost_time=0,
|
|
|
|
|
request_params=params.query_text,
|
|
|
|
|
response_result={},
|
|
|
|
|
status=0,
|
|
|
|
|
del_flag=0
|
|
|
|
|
):
|
|
|
|
|
description_list = set(params.query_text.split("\n"))
|
|
|
|
|
query_count = 0
|
|
|
|
|
dataList = []
|
|
|
|
|
try:
|
|
|
|
|
for description in description_list:
|
|
|
|
|
if not description:
|
|
|
|
|
continue
|
|
|
|
|
query_count += 1
|
|
|
|
|
query = {
|
|
|
|
|
"query": {
|
|
|
|
|
"match": {
|
2025-02-17 19:38:34 +08:00
|
|
|
|
"description": {
|
2025-02-15 23:36:20 +08:00
|
|
|
|
"query": description.strip(),
|
|
|
|
|
"fuzziness": "AUTO" # 自动模糊匹配
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"sort": [
|
|
|
|
|
{
|
|
|
|
|
"_score": { # 按照匹配度排序
|
|
|
|
|
"order": "desc" # 降序
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
matches = []
|
|
|
|
|
data = await request.app.state.es.search(index=ElasticSearchConfig.ES_INDEX, body=query, size=5)
|
|
|
|
|
# 获取当前查询的最大 _score
|
|
|
|
|
max_score = data["hits"].get("max_score", 1)
|
|
|
|
|
# 处理每一条匹配结果
|
|
|
|
|
for hit in data["hits"]["hits"]:
|
2025-02-23 23:50:42 +08:00
|
|
|
|
code = await Code.get_or_none(code=hit["_source"]["code"], del_flag=1)
|
2025-02-15 23:36:20 +08:00
|
|
|
|
# 归一化匹配度,转换为百分比
|
|
|
|
|
match_rate = round((hit["_score"] / max_score) * 100, 2) # 归一化后计算百分比
|
|
|
|
|
# 将匹配结果添加到列表中
|
|
|
|
|
matches.append({
|
|
|
|
|
"id": code.id if code else None,
|
2025-02-17 19:38:34 +08:00
|
|
|
|
"code": hit["_source"]["code"], # 获取商品编码
|
|
|
|
|
"description": hit["_source"]["description"], # 获取商品描述
|
2025-02-15 23:36:20 +08:00
|
|
|
|
"match_rate": match_rate # 匹配度(百分比)
|
|
|
|
|
})
|
|
|
|
|
query_code = await QueryCode.create(
|
|
|
|
|
query_text=description.strip(),
|
|
|
|
|
result_text=jsonable_encoder(matches),
|
2025-02-19 01:20:04 +08:00
|
|
|
|
session_id=log.id,
|
|
|
|
|
status=1 if matches else 0
|
2025-02-15 23:36:20 +08:00
|
|
|
|
)
|
|
|
|
|
dataList.append({
|
|
|
|
|
"id": query_code.id,
|
|
|
|
|
"query_text": description.strip(),
|
|
|
|
|
"result_text": jsonable_encoder(matches),
|
|
|
|
|
"status": 1 if matches else 0,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
cost_time = float(time.time() - start_time) * 100
|
|
|
|
|
log.operator_id = user_id
|
|
|
|
|
log.query_count = query_count
|
|
|
|
|
log.result_count = len(dataList)
|
|
|
|
|
log.cost_time = cost_time
|
|
|
|
|
log.status = 1 if dataList else 0
|
|
|
|
|
log.response_result = jsonable_encoder(dataList)
|
|
|
|
|
log.del_flag = 1
|
|
|
|
|
await log.save()
|
|
|
|
|
return Response.success(data={
|
|
|
|
|
"id": log.id,
|
|
|
|
|
"result_count": len(dataList),
|
|
|
|
|
"query": params.query_text,
|
|
|
|
|
"response_result": jsonable_encoder(dataList),
|
|
|
|
|
"query_count": query_count,
|
|
|
|
|
"cost_time": cost_time,
|
|
|
|
|
"status": 1 if dataList else 0,
|
|
|
|
|
"operation_time": log.operation_time
|
|
|
|
|
})
|
|
|
|
|
except ServiceException as e:
|
|
|
|
|
logger.error(e.message)
|
|
|
|
|
await log.delete()
|
|
|
|
|
raise ServiceException(message="查询失败!")
|
|
|
|
|
return Response.failure(msg="查询失败!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/query/{id}", response_class=JSONResponse, response_model=QueryCodeResponse, summary="查询编码")
|
|
|
|
|
@Log(title="查询编码", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:importQuery"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def get_code_list(request: Request,
|
|
|
|
|
id: str = Path(description="文件ID"),
|
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user),
|
|
|
|
|
):
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
user_id = current_user.get("id")
|
2025-02-23 23:50:42 +08:00
|
|
|
|
if file := await File.get_or_none(id=id, del_flag=1):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
uploader_id = await file.first().values(id="uploader__id")
|
|
|
|
|
if str(uploader_id["id"]) == user_id:
|
|
|
|
|
if log := await QueryCodeLog.create(
|
|
|
|
|
operator_id=user_id,
|
|
|
|
|
query_count=0,
|
|
|
|
|
result_count=0,
|
|
|
|
|
cost_time=0,
|
|
|
|
|
request_params="",
|
|
|
|
|
response_result={},
|
|
|
|
|
status=0,
|
|
|
|
|
del_flag=0
|
|
|
|
|
):
|
|
|
|
|
try:
|
|
|
|
|
query_text = ""
|
|
|
|
|
query_count = 0
|
|
|
|
|
dataList = []
|
|
|
|
|
df = pd.read_excel(file.absolute_path)
|
|
|
|
|
for index, row in df.iterrows():
|
|
|
|
|
query_count += 1
|
|
|
|
|
query_text += row["text"] + "\n"
|
|
|
|
|
query = {
|
|
|
|
|
"query": {
|
|
|
|
|
"match": {
|
2025-02-17 19:38:34 +08:00
|
|
|
|
"description": {
|
2025-02-15 23:36:20 +08:00
|
|
|
|
"query": row["text"].strip(),
|
|
|
|
|
"fuzziness": "AUTO" # 自动模糊匹配
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"sort": [
|
|
|
|
|
{
|
|
|
|
|
"_score": { # 按照匹配度排序
|
|
|
|
|
"order": "desc" # 降序
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
matches = []
|
|
|
|
|
data = await request.app.state.es.search(index=ElasticSearchConfig.ES_INDEX, body=query, size=5)
|
|
|
|
|
# 获取当前查询的最大 _score
|
|
|
|
|
max_score = data["hits"].get("max_score", 1)
|
|
|
|
|
# 处理每一条匹配结果
|
|
|
|
|
for hit in data["hits"]["hits"]:
|
2025-02-23 23:50:42 +08:00
|
|
|
|
code = await Code.get_or_none(code=hit["_source"]["code"], del_flag=1)
|
2025-02-15 23:36:20 +08:00
|
|
|
|
# 归一化匹配度,转换为百分比
|
|
|
|
|
match_rate = round((hit["_score"] / max_score) * 100, 2) # 归一化后计算百分比
|
|
|
|
|
# 将匹配结果添加到列表中
|
|
|
|
|
matches.append({
|
|
|
|
|
"id": code.id if code else None,
|
2025-02-17 19:38:34 +08:00
|
|
|
|
"code": hit["_source"]["code"], # 获取商品编码
|
|
|
|
|
"description": hit["_source"]["description"], # 获取商品描述
|
2025-02-15 23:36:20 +08:00
|
|
|
|
"match_rate": match_rate # 匹配度(百分比)
|
|
|
|
|
})
|
|
|
|
|
query_code = await QueryCode.create(
|
|
|
|
|
query_text=row['text'].strip(),
|
|
|
|
|
result_text=jsonable_encoder(matches),
|
2025-02-19 01:20:04 +08:00
|
|
|
|
session_id=log.id,
|
|
|
|
|
status=1 if matches else 0
|
2025-02-15 23:36:20 +08:00
|
|
|
|
)
|
|
|
|
|
dataList.append({
|
|
|
|
|
"id": query_code.id,
|
|
|
|
|
"query_text": row['text'].strip(),
|
|
|
|
|
"result_text": jsonable_encoder(matches),
|
|
|
|
|
"status": 1 if matches else 0,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
cost_time = float(time.time() - start_time) * 100
|
|
|
|
|
log.request_params = query_text
|
|
|
|
|
log.operator_id = user_id
|
|
|
|
|
log.query_count = query_count
|
|
|
|
|
log.result_count = len(dataList)
|
|
|
|
|
log.cost_time = cost_time
|
|
|
|
|
log.status = 1 if dataList else 0
|
|
|
|
|
log.response_result = jsonable_encoder(dataList)
|
|
|
|
|
log.del_flag = 1
|
|
|
|
|
await log.save()
|
|
|
|
|
return Response.success(data={
|
|
|
|
|
"id": log.id,
|
|
|
|
|
"result_count": len(dataList),
|
|
|
|
|
"query": query_text,
|
|
|
|
|
"response_result": jsonable_encoder(dataList),
|
|
|
|
|
"query_count": query_count,
|
|
|
|
|
"cost_time": cost_time,
|
|
|
|
|
"status": 1 if dataList else 0,
|
|
|
|
|
"operation_time": log.operation_time
|
|
|
|
|
})
|
|
|
|
|
except ServiceException as e:
|
|
|
|
|
logger.error(e.message)
|
|
|
|
|
await log.delete()
|
|
|
|
|
raise ServiceException(message="查询失败!")
|
|
|
|
|
else:
|
|
|
|
|
raise PermissionException(message="权限不足")
|
|
|
|
|
else:
|
|
|
|
|
return Response.failure(msg="文件不存在")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@codeAPI.get("/logList", response_class=JSONResponse, response_model=GetQueryCodeLogResponse,
|
|
|
|
|
summary="查询编码日志列表")
|
|
|
|
|
@Log(title="查询编码日志列表", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:logList"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def get_code_log_list(request: Request,
|
|
|
|
|
page: int = Query(default=1, description="当前页码"),
|
|
|
|
|
pageSize: int = Query(default=10, description="每页数量"),
|
2025-02-23 23:50:42 +08:00
|
|
|
|
department_id: Optional[str] = Query(default=None, description="部门ID"),
|
|
|
|
|
username: Optional[str] = Query(default=None, description="用户账号"),
|
|
|
|
|
nickname: Optional[str] = Query(default=None, description="用户昵称"),
|
2025-02-15 23:36:20 +08:00
|
|
|
|
startTime: Optional[str] = Query(default=None, description="开始时间"),
|
|
|
|
|
endTime: Optional[str] = Query(default=None, description="结束时间"),
|
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user),
|
|
|
|
|
):
|
2025-02-23 23:50:42 +08:00
|
|
|
|
sub_departments = current_user.get("sub_departments")
|
|
|
|
|
filterArgs = {
|
|
|
|
|
f'{k}__contains': v for k, v in {
|
|
|
|
|
'operator__username': username,
|
|
|
|
|
'operator__nickname': nickname,
|
|
|
|
|
}.items() if v
|
|
|
|
|
}
|
2025-02-15 23:36:20 +08:00
|
|
|
|
if startTime and endTime:
|
|
|
|
|
startTime = float(startTime) / 1000
|
|
|
|
|
endTime = float(endTime) / 1000
|
|
|
|
|
startTime = datetime.fromtimestamp(startTime)
|
|
|
|
|
endTime = datetime.fromtimestamp(endTime)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
filterArgs['operation_time__range'] = [startTime, endTime]
|
2025-02-24 18:33:22 +08:00
|
|
|
|
if await hasAuth(request, "code:btn:logAdmin"):
|
|
|
|
|
if department_id:
|
|
|
|
|
filterArgs['operator__department__id'] = department_id
|
|
|
|
|
else:
|
|
|
|
|
filterArgs['operator__department__id__in'] = sub_departments
|
2025-02-15 23:36:20 +08:00
|
|
|
|
else:
|
2025-02-24 18:33:22 +08:00
|
|
|
|
if department_id:
|
|
|
|
|
filterArgs['operator__department__id'] = department_id
|
2025-02-23 23:50:42 +08:00
|
|
|
|
count = await QueryCodeLog.filter(**filterArgs, operator__del_flag=1, del_flag=1).count()
|
|
|
|
|
data = await QueryCodeLog.filter(**filterArgs, operator__del_flag=1, del_flag=1).order_by("-operation_time").offset(
|
|
|
|
|
(page - 1) * pageSize).limit(pageSize).values(
|
|
|
|
|
id="id",
|
|
|
|
|
query_count="query_count",
|
|
|
|
|
result_count="result_count",
|
|
|
|
|
cost_time="cost_time",
|
|
|
|
|
operation_time="operation_time",
|
|
|
|
|
status="status",
|
|
|
|
|
request_params="request_params",
|
|
|
|
|
response_result="response_result",
|
|
|
|
|
create_time="create_time",
|
|
|
|
|
update_time="update_time",
|
|
|
|
|
operator_id="operator__id",
|
|
|
|
|
operator_name="operator__username",
|
|
|
|
|
operator_nickname="operator__nickname",
|
|
|
|
|
department_id="operator__department__id",
|
|
|
|
|
department_name="operator__department__name",
|
|
|
|
|
)
|
2025-02-15 23:36:20 +08:00
|
|
|
|
return Response.success(data={
|
|
|
|
|
"page": page,
|
|
|
|
|
"pageSize": pageSize,
|
|
|
|
|
"result": data,
|
|
|
|
|
"total": count
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2025-02-18 02:04:40 +08:00
|
|
|
|
@codeAPI.get("/logList/all", response_class=JSONResponse, response_model=GetCodeLogAllResponse,
|
|
|
|
|
summary="查询所有编码日志列表")
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Log(title="查询所有编码日志列表", business_type=BusinessType.EXPORT)
|
|
|
|
|
@Auth(permission_list=["code:btn:export"])
|
2025-02-18 02:04:40 +08:00
|
|
|
|
async def get_code_log_list(request: Request,
|
2025-02-23 23:50:42 +08:00
|
|
|
|
department_id: Optional[str] = Query(default=None, description="部门ID"),
|
|
|
|
|
username: Optional[str] = Query(default=None, description="用户账号"),
|
|
|
|
|
nickname: Optional[str] = Query(default=None, description="用户昵称"),
|
2025-02-18 02:04:40 +08:00
|
|
|
|
startTime: Optional[str] = Query(default=None, description="开始时间"),
|
|
|
|
|
endTime: Optional[str] = Query(default=None, description="结束时间"),
|
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user),
|
|
|
|
|
):
|
2025-02-23 23:50:42 +08:00
|
|
|
|
sub_departments = current_user.get("sub_departments")
|
|
|
|
|
filterArgs = {
|
|
|
|
|
f'{k}__contains': v for k, v in {
|
|
|
|
|
'operator__username': username,
|
|
|
|
|
'operator__nickname': nickname,
|
|
|
|
|
}.items() if v
|
|
|
|
|
}
|
2025-02-18 02:04:40 +08:00
|
|
|
|
if startTime and endTime:
|
|
|
|
|
startTime = float(startTime) / 1000
|
|
|
|
|
endTime = float(endTime) / 1000
|
|
|
|
|
startTime = datetime.fromtimestamp(startTime)
|
|
|
|
|
endTime = datetime.fromtimestamp(endTime)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
filterArgs['operation_time__range'] = [startTime, endTime]
|
2025-02-24 18:33:22 +08:00
|
|
|
|
if await hasAuth(request, "code:btn:logAdmin"):
|
|
|
|
|
if department_id:
|
|
|
|
|
filterArgs['operator__department__id'] = department_id
|
|
|
|
|
else:
|
|
|
|
|
filterArgs['operator__department__id__in'] = sub_departments
|
2025-02-18 02:04:40 +08:00
|
|
|
|
else:
|
2025-02-24 18:33:22 +08:00
|
|
|
|
if department_id:
|
|
|
|
|
filterArgs['operator__department__id'] = department_id
|
2025-02-23 23:50:42 +08:00
|
|
|
|
count = await QueryCodeLog.filter(**filterArgs, operator__del_flag=1, del_flag=1).count()
|
|
|
|
|
data = await QueryCodeLog.filter(**filterArgs, operator__del_flag=1, del_flag=1).order_by("-operation_time").values(
|
|
|
|
|
id="id",
|
|
|
|
|
query_count="query_count",
|
|
|
|
|
result_count="result_count",
|
|
|
|
|
cost_time="cost_time",
|
|
|
|
|
operation_time="operation_time",
|
|
|
|
|
status="status",
|
|
|
|
|
request_params="request_params",
|
|
|
|
|
response_result="response_result",
|
|
|
|
|
create_time="create_time",
|
|
|
|
|
update_time="update_time",
|
|
|
|
|
operator_id="operator__id",
|
|
|
|
|
operator_name="operator__username",
|
|
|
|
|
operator_nickname="operator__nickname",
|
|
|
|
|
department_id="operator__department__id",
|
|
|
|
|
department_name="operator__department__name",
|
|
|
|
|
)
|
2025-02-18 02:04:40 +08:00
|
|
|
|
|
|
|
|
|
return Response.success(data={
|
|
|
|
|
"result": data,
|
|
|
|
|
"total": count
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2025-02-15 23:36:20 +08:00
|
|
|
|
@codeAPI.get("/logInfo/{id}", response_class=JSONResponse, response_model=GetQueryCodeLogDetailResponse,
|
|
|
|
|
summary="查询编码日志详情")
|
|
|
|
|
@Log(title="查询编码日志详情", business_type=BusinessType.SELECT)
|
2025-02-23 23:50:42 +08:00
|
|
|
|
@Auth(permission_list=["code:btn:logInfo"])
|
2025-02-15 23:36:20 +08:00
|
|
|
|
async def get_code_log_detail(request: Request,
|
|
|
|
|
id: str = Path(..., description="日志ID"),
|
2025-02-23 23:50:42 +08:00
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user),
|
2025-02-15 23:36:20 +08:00
|
|
|
|
):
|
2025-02-23 23:50:42 +08:00
|
|
|
|
sub_departments = current_user.get("sub_departments")
|
|
|
|
|
if log := await QueryCodeLog.get_or_none(id=id, operator__department__id__in=sub_departments, del_flag=1):
|
2025-02-15 23:36:20 +08:00
|
|
|
|
data = await log.first().values(
|
|
|
|
|
id="id",
|
|
|
|
|
query_count="query_count",
|
|
|
|
|
result_count="result_count",
|
|
|
|
|
cost_time="cost_time",
|
|
|
|
|
operation_time="operation_time",
|
|
|
|
|
status="status",
|
|
|
|
|
request_params="request_params",
|
|
|
|
|
response_result="response_result",
|
|
|
|
|
create_time="create_time",
|
|
|
|
|
update_time="update_time",
|
|
|
|
|
operator_id="operator__id",
|
|
|
|
|
operator_name="operator__username",
|
|
|
|
|
operator_nickname="operator__nickname",
|
|
|
|
|
department_id="operator__department__id",
|
|
|
|
|
department_name="operator__department__name",
|
|
|
|
|
)
|
|
|
|
|
return Response.success(data=data)
|
|
|
|
|
return Response.failure(msg="日志不存在!")
|