皓月归尘 92a57fa5d0 feat(models): 添加海关税率管理相关模型和接口
- 在 models 中新增 Version、HtsClass 和 HtsItem 三个模型
- 在 api 中新增 hts 路由和相关接口
- 更新 app.py 和 code.py 以支持海关税率管理功能
2025-03-31 04:25:09 +08:00

742 lines
24 KiB
Python

# _*_ coding : UTF-8 _*_
# @Time : 2025/03/24 02:29:03
# @UpdateTime : 2025/03/24 02:29:03
# @Author : sonder
# @File : version.py
# @Comment : 本程序用于生成版本信息增删改查接口
import json
import uuid
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, Path, Request, Query
from fastapi.responses import JSONResponse
from annotation.auth import Auth
from annotation.log import Log
from config.constant import BusinessType
from controller.login import LoginController
from models import Version, HtsClass, HtsItem, File
from schemas.common import BaseResponse, DeleteListParams
from schemas.hts import AddHTSClassParams, UpdateHTSClassParams, GetHTSClassInfoResponse, GetHTSClassListResponse, \
ImportHtsItemParams
from schemas.hts import AddHtsItemParams, UpdateHtsItemParams, GetHtsItemInfoResponse, GetHtsItemListResponse
from schemas.hts import AddVersionParams, UpdateVersionParams, GetVersionInfoResponse, GetVersionListResponse
from utils.response import Response
htsAPI = APIRouter(
prefix="/hts",
dependencies=[Depends(LoginController.get_current_user)],
)
@htsAPI.post("/version/add", response_class=JSONResponse, response_model=BaseResponse, summary="新增税率编码版本")
@Log(title="新增税率编码版本", business_type=BusinessType.INSERT)
@Auth(permission_list=["version:btn:add"])
async def add_version(request: Request, params: AddVersionParams):
if await Version.get_or_none(
version=params.version,
date=params.date,
url=params.url,
del_flag=1
):
return Response.error(msg="税率编码版本已存在!")
version = await Version.create(
version=params.version,
date=params.date,
url=params.url,
)
if version:
return Response.success(msg="新增成功!")
else:
return Response.error(msg="新增失败")
@htsAPI.delete("/version/delete/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="删除税率编码版本")
@htsAPI.post("/version/delete/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="删除税率编码版本")
@Log(title="删除税率编码版本", business_type=BusinessType.DELETE)
@Auth(permission_list=["version:btn:delete"])
async def delete_version(request: Request, id: str = Path(description="税率编码版本ID")):
if version := await Version.get_or_none(id=id, del_flag=1):
version.del_flag = 0
await version.save()
return Response.success(msg="删除成功")
else:
return Response.error(msg="税率编码版本不存在!")
@htsAPI.delete("/version/deleteList", response_class=JSONResponse, response_model=BaseResponse,
summary="批量删除税率编码版本")
@htsAPI.post("/version/deleteList", response_class=JSONResponse, response_model=BaseResponse,
summary="批量删除税率编码版本")
@Log(title="批量删除税率编码版本", business_type=BusinessType.DELETE)
@Auth(permission_list=["version:btn:delete"])
async def delete_version_list(request: Request, params: DeleteListParams):
for id in set(params.ids):
if version := await Version.get_or_none(id=id, del_flag=1):
version.del_flag = 0
await version.save()
return Response.success(msg="删除成功")
@htsAPI.put("/version/update/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="修改税率编码版本")
@htsAPI.post("/version/update/{id}", response_class=JSONResponse, response_model=BaseResponse,
summary="修改税率编码版本")
@Log(title="修改税率编码版本", business_type=BusinessType.UPDATE)
@Auth(permission_list=["version:btn:update"])
async def update_version(request: Request, params: UpdateVersionParams, id: str = Path(description="税率编码版本ID")):
if version := await Version.get_or_none(id=id, del_flag=1):
print(params)
version.version = params.version
version.date = params.date
version.url = params.url
await version.save()
return Response.success(msg="修改成功")
else:
return Response.error(msg="税率编码版本不存在")
@htsAPI.get("/version/info/{id}", response_class=JSONResponse, response_model=GetVersionInfoResponse,
summary="获取税率编码版本信息")
@Log(title="获取税率编码版本信息", business_type=BusinessType.SELECT)
@Auth(permission_list=["version:btn:info"])
async def get_version_info(request: Request, id: str = Path(description="税率编码版本ID")):
if version := await Version.get_or_none(id=id, del_flag=1):
data = {
"id": version.id,
"create_time": version.create_time,
"update_time": version.update_time,
"version": version.version,
"date": version.date,
"url": version.url,
}
return Response.success(data=data)
else:
return Response.error(msg="税率编码版本不存在")
@htsAPI.get("/version/list", response_class=JSONResponse, response_model=GetVersionListResponse,
summary="获取税率编码版本列表")
@Log(title="获取税率编码版本列表", business_type=BusinessType.SELECT)
@Auth(permission_list=["version:btn:list"])
async def get_version_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页数量"),
version: Optional[str] = Query(default=None, description="版本号"),
date: Optional[str] = Query(default=None, description="版本日期"),
url: Optional[str] = Query(default=None, description="下载地址"),
):
filterArgs = {
"version__icontains": version,
"date__icontains": date,
"url__icontains": url,
}
filterArgs = {k: v for k, v in filterArgs.items() if v is not None}
total = await Version.filter(**filterArgs, del_flag=1).count()
data = await Version.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
create_time="create_time",
update_time="update_time",
version="version",
date="date",
url="url",
)
return Response.success(data={
"total": total,
"result": data,
"page": page,
"pageSize": pageSize,
})
@htsAPI.post("/class/add", response_class=JSONResponse, response_model=BaseResponse, summary="新增编码类别")
@Log(title="新增编码类别", business_type=BusinessType.INSERT)
@Auth(permission_list=["htsclass:btn:add"])
async def add_htsclass(request: Request, params: AddHTSClassParams):
if await HtsClass.get_or_none(
class_name=params.class_name,
class_description=params.class_description,
chapter_name=params.chapter_name,
chapter_description=params.chapter_description,
del_flag=1
):
return Response.error(msg="编码类别已存在!")
htsclass = await HtsClass.create(
class_name=params.class_name,
class_description=params.class_description,
chapter_name=params.chapter_name,
chapter_description=params.chapter_description,
)
if htsclass:
return Response.success(msg="新增成功!")
else:
return Response.error(msg="新增失败")
@htsAPI.delete("/class/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除编码类别")
@htsAPI.post("/class/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除编码类别")
@Log(title="删除编码类别", business_type=BusinessType.DELETE)
@Auth(permission_list=["htsclass:btn:delete"])
async def delete_htsclass(request: Request, id: str = Path(description="编码类别ID")):
if htsclass := await HtsClass.get_or_none(id=id, del_flag=1):
htsclass.del_flag = 0
await htsclass.save()
return Response.success(msg="删除成功")
else:
return Response.error(msg="编码类别不存在!")
@htsAPI.delete("/class/deleteList", response_class=JSONResponse, response_model=BaseResponse,
summary="批量删除编码类别")
@htsAPI.post("/class/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除编码类别")
@Log(title="批量删除编码类别", business_type=BusinessType.DELETE)
@Auth(permission_list=["htsclass:btn:delete"])
async def delete_htsclass_list(request: Request, params: DeleteListParams):
for id in set(params.ids):
if htsclass := await HtsClass.get_or_none(id=id, del_flag=1):
htsclass.del_flag = 0
await htsclass.save()
return Response.success(msg="删除成功")
@htsAPI.put("/class/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改编码类别")
@htsAPI.post("/class/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改编码类别")
@Log(title="修改编码类别", business_type=BusinessType.UPDATE)
@Auth(permission_list=["htsclass:btn:update"])
async def update_htsclass(request: Request, params: UpdateHTSClassParams, id: str = Path(description="编码类别ID")):
if htsclass := await HtsClass.get_or_none(id=id, del_flag=1):
htsclass.class_name = params.class_name
htsclass.class_description = params.class_description
htsclass.chapter_name = params.chapter_name
htsclass.chapter_description = params.chapter_description
await htsclass.save()
return Response.success(msg="修改成功")
else:
return Response.error(msg="编码类别不存在")
@htsAPI.get("/class/info/{id}", response_class=JSONResponse, response_model=GetHTSClassInfoResponse,
summary="获取编码类别信息")
@Log(title="获取编码类别信息", business_type=BusinessType.SELECT)
@Auth(permission_list=["htsclass:btn:info"])
async def get_htsclass_info(request: Request, id: str = Path(description="编码类别ID")):
if htsclass := await HtsClass.get_or_none(id=id, del_flag=1):
data = {
"id": htsclass.id,
"create_time": htsclass.create_time,
"update_time": htsclass.update_time,
"class_name": htsclass.class_name,
"class_description": htsclass.class_description,
"chapter_name": htsclass.chapter_name,
"chapter_description": htsclass.chapter_description,
}
return Response.success(data=data)
else:
return Response.error(msg="编码类别不存在")
@htsAPI.get("/class/list", response_class=JSONResponse, response_model=GetHTSClassListResponse,
summary="获取编码类别列表")
@Log(title="获取编码类别列表", business_type=BusinessType.SELECT)
@Auth(permission_list=["htsclass:btn:list"])
async def get_htsclass_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页数量"),
class_name: Optional[str] = Query(default=None, description="类名"),
class_description: Optional[str] = Query(default=None, description="类描述"),
chapter_name: Optional[str] = Query(default=None, description="章节"),
chapter_description: Optional[str] = Query(default=None, description="章节描述"),
):
filterArgs = {
"class_name__icontains": class_name,
"class_description__icontains": class_description,
"chapter_name__icontains": chapter_name,
"chapter_description__icontains": chapter_description,
}
filterArgs = {k: v for k, v in filterArgs.items() if v is not None}
total = await HtsClass.filter(**filterArgs, del_flag=1).count()
data = await HtsClass.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
create_time="create_time",
update_time="update_time",
class_name="class_name",
class_description="class_description",
chapter_name="chapter_name",
chapter_description="chapter_description",
)
return Response.success(data={
"total": total,
"result": data,
"page": page,
"pageSize": pageSize,
})
@htsAPI.post("/item/add", response_class=JSONResponse, response_model=BaseResponse, summary="新增编码项")
@Log(title="新增编码项", business_type=BusinessType.INSERT)
@Auth(permission_list=["htsitem:btn:add"])
async def add_htsitem(request: Request, params: AddHtsItemParams):
if await HtsItem.get_or_none(
parent_id=params.parent_id,
htsno=params.htsno,
indent=params.indent,
description=params.description,
units=params.units,
general=params.general,
special=params.special,
other=params.other,
quota_quantity=params.quota_quantity,
additional_duties=params.additional_duties,
footnotes=params.footnotes,
class_id=params.class_id,
version_id=params.version_id,
del_flag=1
):
return Response.error(msg="编码项目已存在!")
htsitem = await HtsItem.create(
parent_id=params.parent_id,
htsno=params.htsno,
indent=params.indent,
description=params.description,
units=params.units,
general=params.general,
special=params.special,
other=params.other,
quota_quantity=params.quota_quantity,
additional_duties=params.additional_duties,
footnotes=params.footnotes,
class_id=params.class_id,
version_id=params.version_id,
)
if htsitem:
return Response.success(msg="新增成功!")
else:
return Response.error(msg="新增失败")
@htsAPI.delete("/item/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除编码项")
@htsAPI.post("/item/delete/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="删除编码项")
@Log(title="删除编码项", business_type=BusinessType.DELETE)
@Auth(permission_list=["htsitem:btn:delete"])
async def delete_htsitem(request: Request, id: str = Path(description="编码项目ID")):
if htsitem := await HtsItem.get_or_none(id=id, del_flag=1):
htsitem.del_flag = 0
await htsitem.save()
return Response.success(msg="删除成功")
else:
return Response.error(msg="编码项目不存在!")
@htsAPI.delete("/item/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除编码项")
@htsAPI.post("/item/deleteList", response_class=JSONResponse, response_model=BaseResponse, summary="批量删除编码项")
@Log(title="批量删除编码项", business_type=BusinessType.DELETE)
@Auth(permission_list=["htsitem:btn:delete"])
async def delete_htsitem_list(request: Request, params: DeleteListParams):
for id in set(params.ids):
if htsitem := await HtsItem.get_or_none(id=id, del_flag=1):
htsitem.del_flag = 0
await htsitem.save()
return Response.success(msg="删除成功")
@htsAPI.put("/item/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改编码项")
@htsAPI.post("/item/update/{id}", response_class=JSONResponse, response_model=BaseResponse, summary="修改编码项")
@Log(title="修改编码项", business_type=BusinessType.UPDATE)
@Auth(permission_list=["htsitem:btn:update"])
async def update_htsitem(request: Request, params: UpdateHtsItemParams, id: str = Path(description="编码项目ID")):
if htsitem := await HtsItem.get_or_none(id=id, del_flag=1):
htsitem.parent_id = params.parent_id
htsitem.htsno = params.htsno
htsitem.indent = params.indent
htsitem.description = params.description
htsitem.units = params.units
htsitem.general = params.general
htsitem.special = params.special
htsitem.other = params.other
htsitem.quota_quantity = params.quota_quantity
htsitem.additional_duties = params.additional_duties
htsitem.footnotes = params.footnotes
if class_ := await HtsClass.get_or_none(id=params.class_id, del_flag=1):
htsitem.class_ = class_
if version := await Version.get_or_none(id=params.version_id, del_flag=1):
htsitem.version = version
htsitem.version_id = params.version_id
await htsitem.save()
return Response.success(msg="修改成功")
else:
return Response.error(msg="编码项目不存在")
@htsAPI.get("/item/info/{id}", response_class=JSONResponse, response_model=GetHtsItemInfoResponse,
summary="获取编码项信息")
@Log(title="获取编码项信息", business_type=BusinessType.SELECT)
@Auth(permission_list=["htsitem:btn:info"])
async def get_htsitem_info(request: Request, id: str = Path(description="编码项目ID")):
if htsitem := await HtsItem.get_or_none(id=id, del_flag=1):
data = {
"id": htsitem.id,
"create_time": htsitem.create_time,
"update_time": htsitem.update_time,
"parent_id": htsitem.parent_id,
"htsno": htsitem.htsno,
"indent": htsitem.indent,
"description": htsitem.description,
"units": htsitem.units,
"general": htsitem.general,
"special": htsitem.special,
"other": htsitem.other,
"quota_quantity": htsitem.quota_quantity,
"additional_duties": htsitem.additional_duties,
"footnotes": htsitem.footnotes,
"class_id": htsitem.class_id,
"version_id": htsitem.version_id,
}
return Response.success(data=data)
else:
return Response.error(msg="编码项目不存在")
@htsAPI.get("/item/list", response_class=JSONResponse, response_model=GetHtsItemListResponse, summary="获取编码项列表")
@Log(title="获取编码项列表", business_type=BusinessType.SELECT)
@Auth(permission_list=["htsitem:btn:list"])
async def get_htsitem_list(
request: Request,
page: int = Query(default=1, description="当前页码"),
pageSize: int = Query(default=10, description="每页数量"),
htsno: Optional[str] = Query(default=None, description="编码"),
description: Optional[str] = Query(default=None, description="描述"),
units: Optional[str] = Query(default=None, description="单位列表"),
general: Optional[str] = Query(default=None, description="通用税率"),
special: Optional[str] = Query(default=None, description="特殊税率,适用于特定国家或地区"),
other: Optional[str] = Query(default=None, description="其他税率"),
quota_quantity: Optional[str] = Query(default=None, description="配额数量"),
additional_duties: Optional[str] = Query(default=None, description="附加税"),
footnotes: Optional[str] = Query(default=None, description="脚注列表"),
class_id: Optional[str] = Query(default=None, description="所属类"),
version_id: Optional[str] = Query(default=None, description="所属版本"),
):
filterArgs = {
"htsno__icontains": htsno,
"description__icontains": description,
"units__icontains": units,
"general__icontains": general,
"special__icontains": special,
"other__icontains": other,
"quota_quantity__icontains": quota_quantity,
"additional_duties__icontains": additional_duties,
"footnotes__icontains": footnotes,
"class__id": class_id,
"version_id": version_id,
}
filterArgs = {k: v for k, v in filterArgs.items() if v is not None}
total = await HtsItem.filter(**filterArgs, del_flag=1).count()
data = await HtsItem.filter(**filterArgs, del_flag=1).offset((page - 1) * pageSize).limit(pageSize).values(
id="id",
create_time="create_time",
update_time="update_time",
parent_id="parent_id",
htsno="htsno",
indent="indent",
description="description",
units="units",
general="general",
special="special",
other="other",
quota_quantity="quota_quantity",
additional_duties="additional_duties",
footnotes="footnotes",
class_id="class__id",
version_id="version_id",
)
return Response.success(data={
"total": total,
"result": data,
"page": page,
"pageSize": pageSize,
})
@htsAPI.post("/item/import", response_class=JSONResponse, response_model=BaseResponse, summary="导入编码项")
@Log(title="导入编码项", business_type=BusinessType.INSERT)
@Auth(permission_list=["htsitem:btn:import"])
async def add_htsitem(request: Request, params: ImportHtsItemParams):
version = await Version.get_or_none(id=params.version_id, del_flag=1)
if not version:
return Response.error(msg="版本不存在")
class_ = await HtsClass.get_or_none(id=params.class_id, del_flag=1)
if not class_:
return Response.error(msg="编码类不存在")
file = await File.get_or_none(id=params.file_id, del_flag=1)
if not file:
return Response.error(msg="文件不存在")
if await HtsItem.get_or_none(class_=params.class_id, version=params.version_id):
return Response.error(msg="编码项已存在")
def build_hierarchy_with_ids(data):
hierarchy = []
stack = []
for item in data:
current_level = int(item['indent'])
current_item = {
'id': str(uuid.uuid4()), # 生成唯一的 UUID
'indent': item['indent'],
'htsno': item['htsno'].replace(".", "").strip(),
'description': item['description'],
'parent_id': None, # 默认没有父级,
"units": json.dumps(item["units"], ensure_ascii=False),
"general": item["general"],
"special": item["special"],
"other": item["other"],
"footnotes": json.dumps(item["footnotes"], ensure_ascii=False),
"quota_quantity": item["quotaQuantity"],
"additional_duties": item["additionalDuties"],
"addiitional_duties": item["addiitionalDuties"]
}
# 找到当前项的父级
while stack and int(stack[-1]['indent']) >= current_level:
stack.pop()
if stack:
# 设置当前项的 parent_id 为父级的 id
current_item['parent_id'] = stack[-1]['item']['id']
# 将当前项添加到层级结构中
hierarchy.append(current_item)
# 将当前项压入栈中
stack.append({'indent': current_level, 'item': current_item})
return hierarchy
with open(file.absolute_path, 'r', encoding='utf-8') as r:
data = json.load(r)
for x in data:
x["indent"] = str(int(x["indent"]) + 1)
data.append({
"htsno": str(class_.chapter_name).replace("Chapter", "").strip() if int(
str(class_.chapter_name).replace("Chapter", "").strip(" ")) > 10 else "0" +
str(class_.chapter_name).replace(
"Chapter", "").strip(),
"indent": "0",
"description": class_.class_description,
"units": [],
"general": "",
"special": "",
"other": "",
"footnotes": [],
"quotaQuantity": "",
"additionalDuties": "",
"addiitionalDuties": ""
})
hierarchy = build_hierarchy_with_ids(data)
# 批量插入查询结果
create_tasks = [
HtsItem(
id=item["id"],
parent_id=item["parent_id"],
htsno=item["htsno"],
indent=item["indent"],
description=item["description"],
units=item["units"],
general=item["general"],
special=item["special"],
other=item["other"],
quota_quantity=item["quota_quantity"],
additional_duties=item["additional_duties"],
footnotes=item["footnotes"],
class_=class_,
version=version
)
for item in hierarchy
]
await HtsItem.bulk_create(create_tasks)
return Response.success()