From 6122e390fb5a9e8def7ae332b118235b82f4cf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=93=E6=9C=88=E5=BD=92=E5=B0=98?= Date: Sat, 15 Feb 2025 23:36:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/code.py | 521 ++++++++++++++++++++++++++++++++++++++++++++ api/file.py | 2 +- app.py | 2 + controller/login.py | 22 +- controller/query.py | 3 + models/__init__.py | 6 +- models/code.py | 157 +++++++++++++ requirements.txt | Bin 1478 -> 2584 bytes schemas/code.py | 174 +++++++++++++++ schemas/common.py | 1 + 10 files changed, 881 insertions(+), 7 deletions(-) create mode 100644 api/code.py create mode 100644 models/code.py create mode 100644 schemas/code.py diff --git a/api/code.py b/api/code.py new file mode 100644 index 0000000..b16c7d1 --- /dev/null +++ b/api/code.py @@ -0,0 +1,521 @@ +# _*_ 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 + +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 +from schemas.code import GetCodeInfoResponse, GetCodeListResponse, GetQueryCodeParams, \ + DeleteCodeListParams, QueryCodeResponse, AddCodeParams, GetQueryCodeLogResponse, GetQueryCodeLogDetailResponse +from schemas.common import BaseResponse +from utils.log import logger +from utils.response import Response + +codeAPI = APIRouter( + prefix="/code", + dependencies=[Depends(LoginController.get_current_user)] +) + + +@codeAPI.get("/template", summary="获取上传编码模板") +@Log(title="获取上传编码模板", business_type=BusinessType.SELECT) +async def get_upload_template(request: Request): + 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) +async def get_query_template(request: Request): + 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) +async def add_code(request: Request, params: AddCodeParams): + params.code = params.code.replace(".", "").replace("/", "").replace("_", "").replace("-", "").replace("?", + "").replace( + ":", "").replace(":", "").replace("?", "").strip() + if await Code.get_or_none(code=params.code): + 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) +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") + if file := await File.get_or_none(id=id): + uploader_id = await file.first().values(id="uploader__id") + if str(uploader_id["id"]) == user_id: + try: + df = pd.read_excel(file.absolute_path) + for index, row in df.iterrows(): + row["code"] = str(row["code"]).replace(".", "").replace("/", "").replace("_", "").replace("-", + "").replace( + "?", + "").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) +async def delete_code_by_id(request: Request, id: str = Path(description="编码ID"), ): + if code := await Code.get_or_none(id=id): + await code.delete() + 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) +async def delete_code_by_ids(request: Request, params: DeleteCodeListParams): + for id in set(params.ids): + if code := await Code.get_or_none(id=id): + await code.delete() + 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) +async def update_code(request: Request, + params: AddCodeParams, + id: str = Path(description="编码ID")): + if code := await Code.get_or_none(id=id): + 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) +async def get_code_info(request: Request, id: str = Path(description="编码ID")): + if code := await Code.get_or_none(id=id).values( + 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) +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="编码"), + description: Optional[str] = Query(default=None, description="商品描述")): + filterArgs = { + f'{k}__contains': v for k, v in { + 'code': code, + 'description': description + }.items() if v + } + total = await Code.filter(**filterArgs).count() + data = await Code.filter(**filterArgs).offset((page - 1) * pageSize).limit(pageSize).values( + 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) +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": { + "brief_description": { + "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"]: + code = await Code.get_or_none(code=hit["_source"]["hts8"]) + # 归一化匹配度,转换为百分比 + match_rate = round((hit["_score"] / max_score) * 100, 2) # 归一化后计算百分比 + # 将匹配结果添加到列表中 + matches.append({ + "id": code.id if code else None, + "code": hit["_source"]["hts8"], # 获取商品编码 + "description": hit["_source"]["brief_description"], # 获取商品描述 + "match_rate": match_rate # 匹配度(百分比) + }) + query_code = await QueryCode.create( + query_text=description.strip(), + result_text=jsonable_encoder(matches), + session_id=log.id + ) + 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) +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") + if file := await File.get_or_none(id=id): + 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": { + "brief_description": { + "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"]: + code = await Code.get_or_none(code=hit["_source"]["hts8"]) + # 归一化匹配度,转换为百分比 + match_rate = round((hit["_score"] / max_score) * 100, 2) # 归一化后计算百分比 + # 将匹配结果添加到列表中 + matches.append({ + "id": code.id if code else None, + "code": hit["_source"]["hts8"], # 获取商品编码 + "description": hit["_source"]["brief_description"], # 获取商品描述 + "match_rate": match_rate # 匹配度(百分比) + }) + query_code = await QueryCode.create( + query_text=row['text'].strip(), + result_text=jsonable_encoder(matches), + session_id=log.id + ) + 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) +async def get_code_log_list(request: Request, + page: int = Query(default=1, description="当前页码"), + pageSize: int = Query(default=10, description="每页数量"), + startTime: Optional[str] = Query(default=None, description="开始时间"), + endTime: Optional[str] = Query(default=None, description="结束时间"), + current_user: dict = Depends(LoginController.get_current_user), + ): + user_id = current_user.get("id") + if startTime and endTime: + startTime = float(startTime) / 1000 + endTime = float(endTime) / 1000 + startTime = datetime.fromtimestamp(startTime) + endTime = datetime.fromtimestamp(endTime) + count = await QueryCodeLog.filter(operator_id=user_id, del_flag=1, operation_time__gte=startTime, + operation_time__lte=endTime).count() + data = await QueryCodeLog.filter(operator_id=user_id, del_flag=1, operation_time__gte=startTime, + operation_time__lte=endTime).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", + ) + else: + count = await QueryCodeLog.filter(operator_id=user_id, del_flag=1).count() + data = await QueryCodeLog.filter(operator_id=user_id, 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", + ) + + return Response.success(data={ + "page": page, + "pageSize": pageSize, + "result": data, + "total": count + }) + + +@codeAPI.get("/logInfo/{id}", response_class=JSONResponse, response_model=GetQueryCodeLogDetailResponse, + summary="查询编码日志详情") +@Log(title="查询编码日志详情", business_type=BusinessType.SELECT) +async def get_code_log_detail(request: Request, + id: str = Path(..., description="日志ID"), + ): + if log := await QueryCodeLog.get_or_none(id=id): + 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="日志不存在!") diff --git a/api/file.py b/api/file.py index 37ff0df..b0fadea 100644 --- a/api/file.py +++ b/api/file.py @@ -59,7 +59,7 @@ async def upload_file( file_type=file.content_type, absolute_path=absolute_path, relative_path=relative_path, - uploader=current_user.get("id"), + uploader_id=current_user.get("id"), ) result = await file_record.first().values( id="id", diff --git a/app.py b/app.py index abfb1ac..cc0ebbc 100644 --- a/app.py +++ b/app.py @@ -23,6 +23,7 @@ from api.permission import permissionAPI from api.role import roleAPI from api.server import serverAPI from api.user import userAPI +from api.code import codeAPI from config.database import init_db, close_db from config.env import AppConfig from config.get_ElasticSearch import ElasticSearch @@ -91,6 +92,7 @@ api_list = [ {'api': serverAPI, 'tags': ['服务器管理']}, {'api': i18nAPI, 'tags': ['国际化管理']}, {'api': configApi, 'tags': ['配置管理']}, + {'api': codeAPI, 'tags': ['编码管理']}, ] for api in api_list: diff --git a/controller/login.py b/controller/login.py index bf2dd3c..835f76b 100644 --- a/controller/login.py +++ b/controller/login.py @@ -188,15 +188,27 @@ class LoginController: child_node = await cls.find_node_recursive(child_item["id"], data) if child_node: children.append(child_node) - result = { - "name": item["name"], - "path": item["path"], - "meta": { + meta = { + k: v for k, v in { "title": item["title"], "rank": item["rank"], "icon": item["icon"], + "extraIcon": item["extraIcon"], + "showParent": item["showParent"], + "keepAlive": item["keepAlive"], + "frameSrc": item["frameSrc"], + "frameLoading": item["frameLoading"], "permissions": [item["auths"]], - }, + }.items() if v + } + if item["showLink"]: + meta["showLink"] = True + else: + meta["showLink"] = False + result = { + "name": item["name"], + "path": item["path"], + "meta": meta, "children": children } if item["component"]: diff --git a/controller/query.py b/controller/query.py index 0c04ae0..10c7960 100644 --- a/controller/query.py +++ b/controller/query.py @@ -123,6 +123,9 @@ class QueryController: leaveTransition="permission__leave_transition", activePath="permission__active_path", auths="permission__auths", + frameSrc="permission__frame_src", + frameLoading="permission__frame_loading", + fixedTag="permission__fixed_tag", keepAlive="permission__keep_alive", hiddenTag="permission__hidden_tag", showLink="permission__show_link", diff --git a/models/__init__.py b/models/__init__.py index 7ecd587..c4ab018 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -6,6 +6,7 @@ # @Software : PyCharm # @Comment : 本程序 +from models.code import Code, QueryCode, QueryCodeLog from models.config import Config from models.department import Department, DepartmentRole from models.file import File @@ -28,5 +29,8 @@ __all__ = [ 'UserRole', 'I18n', 'Locale', - 'Config' + 'Config', + 'Code', + 'QueryCode', + 'QueryCodeLog' ] diff --git a/models/code.py b/models/code.py new file mode 100644 index 0000000..af277cc --- /dev/null +++ b/models/code.py @@ -0,0 +1,157 @@ +# _*_ coding : UTF-8 _*_ +# @Time : 2025/02/13 21:23 +# @UpdateTime : 2025/02/13 21:23 +# @Author : sonder +# @File : code.py +# @Software : PyCharm +# @Comment : 本程序 +from tortoise import fields + +from models.common import BaseModel + + +class Code(BaseModel): + """ + 编码模型 + """ + code = fields.CharField( + max_length=255, + description="编码", + source_field="code" + ) + + description = fields.TextField( + description="描述", + source_field="description" + ) + + class Meta: + table = "code" + table_description = "编码表" + + +class QueryCodeLog(BaseModel): + """ + 查询编码日志模型 + """ + request_params = fields.TextField( + null=True, + description="请求参数", + source_field="request_params" # 映射到数据库字段 request_params + ) + """ + 请求参数。 + - 记录用户请求的参数(任意格式,如字符串、JSON、XML 等)。 + - 允许为空。 + - 映射到数据库字段 request_params。 + """ + + response_result = fields.TextField( + null=True, + description="返回结果", + source_field="response_result" # 映射到数据库字段 response_result + ) + """ + 返回结果。 + - 记录操作的返回结果(任意格式,如字符串、JSON、XML 等)。 + - 允许为空。 + - 映射到数据库字段 response_result。 + """ + + status = fields.SmallIntField( + default=1, + description="操作状态(1成功,0失败)", + source_field="status" # 映射到数据库字段 status + ) + """ + 操作状态。 + - 1:成功 + - 0:失败 + - 默认为 1。 + - 映射到数据库字段 status。 + """ + + operation_time = fields.DatetimeField( + auto_now_add=True, + description="操作时间", + source_field="operation_time" # 映射到数据库字段 operation_time + ) + """ + 操作时间。 + - 自动设置为当前时间。 + - 映射到数据库字段 operation_time。 + """ + + cost_time = fields.FloatField( + default=0, + description="消耗时间(毫秒)", + source_field="cost_time" # 映射到数据库字段 cost_time + ) + """ + 消耗时间。 + - 记录操作消耗的时间(单位:毫秒)。 + - 默认为 0。 + - 映射到数据库字段 cost_time。 + """ + query_count = fields.IntField( + default=0, + description="查询统计", + source_field="query_count" # 映射到数据库字段 query_count + ) + """ + 查询统计。 + - 记录查询文本的数量。 + - 默认为 0。 + - 映射到数据库字段 query_count。 + """ + result_count = fields.IntField( + default=0, + description="结果统计", + source_field="result_count" # 映射到数据库字段 result_count + ) + """ + 结果统计。 + - 记录查询结果的数量。 + - 默认为 0。 + - 映射到数据库字段 result_count。 + """ + operator = fields.ForeignKeyField( + "models.User", + related_name="query_code_logs", + description="操作人员", + source_field="operator_id" # 映射到数据库字段 operator_id + ) + """ + 操作人员。 + - 外键关联到 User 表。 + - 允许为空。 + - 映射到数据库字段 operator_id。 + """ + + class Meta: + table = "query_code_log" + table_description = "查询编码日志表" + + +class QueryCode(BaseModel): + """ + 查询编码模型 + """ + session = fields.ForeignKeyField( + "models.QueryCodeLog", + related_name="query_code", + description="会话ID", + source_field="session_id" + ) + query_text = fields.TextField( + description="查询文本", + source_field="query_text" # 映射到数据库字段 query_text + ) + result_text = fields.TextField( + description="结果文本", + source_field="result_text" # 映射到数据库字段 result_text + ) + + class Meta: + table = "query_code" + table_description = "查询编码表" diff --git a/requirements.txt b/requirements.txt index 4a9eeb71439cb01ab27b546c1996d83903fa8abc..b312ed759340836c70c476c0ac3c269db65766ff 100644 GIT binary patch literal 2584 zcmZ{m?@}5;5XAR$s`4mU0>+pRd4W`{O3DYY5D-zgBiy0j!zbzA^eua0Qq+mCJu^K$ zf0jSLCuy1L^pGZLqt_wL0|n!6I-;Yl?CTxoelNk@2H1a z%U6?r$q%S=o#W}ehiK$$k*bIW1|N9qduh@-BCUkI3r`^It?+T2rI7PpG*CHSr8u?D zCxMxzf!+_wVBTd3_VXM3Au@#**!m<`gB&d@9SW&X?(44{Sy{8KsOGcV4DC&j{CoY#d=Ko=fq43VSjEaTU?0 z;I1s;}t;he-gNW_wVtFZErEBM*&{7O< z)3cDMI@3GKY>%1t@vV@1S*@ZrEAh&D6wU4TWBuw6cM#Bx|Y>eUAq(W8~uOZF@L)O);eD)I~dY4 zR))$I7L_pT=*z4>EWKxHeUGl}zr8$_3^U7(Vpmb=vJtVjZd8qJS*Mq<&ATvbzlqwR z{UkcFzoFoW4jZY+cEA(yGy8mckZmIlRv49xubKCR?6vHbQ=41yDE(!HLI0b@=?rnNRGOEWag^Gt`U$i^TacNbmb zxd#6wd~q{-4_H&G=qPj{cM*3@easN%_J%HWBD;?6DQaIteLOR#6&XZ@*5S`N6)k*E zy9;mSZ{O()M>@ti!JXe9IpbE_(!1HY^>0no+C^uaPK`H4&h cA$+z%Grj!wpySD{_+z8MET0VHmUMOg0-{lRegFUf delta 55 zcmbOsa*Uhl|G$Y5S`(LTnXJZCFxiM%Z*m*6%49wkxyemH?8d6JxreonadQ}Z8{_0Z K99fgwI6VMF;}fd@ diff --git a/schemas/code.py b/schemas/code.py new file mode 100644 index 0000000..6f9a6da --- /dev/null +++ b/schemas/code.py @@ -0,0 +1,174 @@ +# _*_ coding : UTF-8 _*_ +# @Time : 2025/02/13 22:07 +# @UpdateTime : 2025/02/13 22:07 +# @Author : sonder +# @File : code.py +# @Software : PyCharm +# @Comment : 本程序 +from datetime import datetime + +from pydantic.alias_generators import to_camel + +from schemas.common import BaseResponse, ListQueryResult +from pydantic import BaseModel, ConfigDict, Field +from typing import List, Optional + + +class CodeInfo(BaseModel): + """ + 编码信息模型 + """ + model_config = ConfigDict( + alias_generator=to_camel + ) + id: str = Field(..., description="主键") + create_by: str = Field(default="", description="创建者") + create_time: Optional[datetime] = Field(default=None, description="创建时间") + update_by: str = Field(default="", description="更新者") + update_time: Optional[datetime] = Field(default=None, description="更新时间") + code: str = Field(..., description="编码") + description: str = Field(..., description="描述") + + +class AddCodeParams(BaseModel): + """ + 更新编码参数 + """ + code: str = Field(..., description="编码") + description: str = Field(..., description="描述") + + model_config = ConfigDict( + alias_generator=to_camel + ) + + +class DeleteCodeListParams(BaseModel): + """ + 删除编码参数 + """ + ids: List[str] = Field(..., description="删除ID列表") + + model_config = ConfigDict( + alias_generator=to_camel + ) + + +class GetCodeInfoResponse(BaseResponse): + """ + 获取编码信息响应 + """ + data: CodeInfo = Field(..., description="编码信息") + + +class GetCodeListResult(ListQueryResult): + """ + 获取编码列表结果 + """ + result: List[CodeInfo] = Field(..., description="编码列表") + + +class GetCodeListResponse(BaseResponse): + """ + 获取编码列表响应 + """ + data: GetCodeListResult = Field(..., description="编码列表") + + +class GetQueryCodeParams(BaseModel): + """ + 获取查询编码结果 + """ + query_text: str = Field(..., description="查询文本") + + +class QueryResultItem(BaseModel): + """ + 查询结果项 + """ + model_config = ConfigDict( + alias_generator=to_camel + ) + id: str = Field(..., description="主键") + code: str = Field(..., description="编码") + description: str = Field(..., description="描述") + match_rate: float = Field(..., description="匹配度") + + +class QueryResult(BaseModel): + """ + 查询结果 + """ + model_config = ConfigDict( + alias_generator=to_camel + ) + id: str = Field(..., description="主键") + query_text: str = Field(..., description="查询文本") + status: int = Field(..., description="查询状态") + result: List[QueryResultItem] = Field(..., description="查询结果") + + +class QueryCodeResult(BaseModel): + """ + 查询编码结果 + """ + model_config = ConfigDict( + alias_generator=to_camel + ) + id: str = Field(..., description="查询ID") + query: str = Field(..., description="查询文本") + query_count: int = Field(..., description="查询统计") + cost_time: float = Field(..., description="消耗时间(毫秒)") + result_count: int = Field(..., description="结果统计") + response_result: List[QueryResult] = Field(..., description="查询结果") + status: int = Field(..., description="查询状态") + operator_time: str = Field(default="", description="操作时间") + + +class QueryCodeResponse(BaseResponse): + """ + 查询编码响应 + """ + data: QueryCodeResult = Field(..., description="查询编码结果") + + +class QueryCodeLogInfo(BaseModel): + """ + 查询编码日志信息 + """ + model_config = ConfigDict( + alias_generator=to_camel + ) + id: str = Field(default="", description="主键") + query_count: int = Field(default=0, description="查询统计") + result_count: int = Field(default=0, description="结果统计") + cost_time: float = Field(default=0.0, description="消耗时间(毫秒)") + operator_time: str = Field(default="", description="操作时间") + request_params: str = Field(default="", description="请求参数") + response_result: str = Field(default="", description="响应结果") + status: int = Field(default=0, description="查询状态") + operator_id: str = Field(default="", description="操作人员ID") + operator_name: str = Field(default="", description="操作人员名称") + operator_nickname: str = Field(default="", description="操作人员昵称") + department_id: str = Field(default="", description="操作人员部门ID") + department_name: str = Field(default="", description="操作人员部门名称") + + +class QueryCodeLogResult(ListQueryResult): + """ + 查询编码日志结果 + """ + result: List[QueryCodeLogInfo] = Field(..., description="查询编码日志列表") + + +class GetQueryCodeLogResponse(BaseResponse): + """ + 查询编码日志响应 + """ + data: QueryCodeLogResult = Field(..., description="查询编码日志结果") + + +class GetQueryCodeLogDetailResponse(BaseResponse): + """ + 查询编码日志详情响应 + """ + data: QueryCodeLogInfo = Field(..., description="查询编码日志详情") diff --git a/schemas/common.py b/schemas/common.py index d5a161a..e5a4e24 100644 --- a/schemas/common.py +++ b/schemas/common.py @@ -28,6 +28,7 @@ class ListQueryResult(BaseModel): result: List = Field(default=[], description="列表数据") total: int = Field(default=0, description="总条数") page: int = Field(default=1, description="当前页码") + pageSize: int = Field(default=10, description="每页数量") class DeleteListParams(BaseModel):