2025-02-13 02:27:44 +08:00
|
|
|
|
# _*_ coding : UTF-8 _*_
|
|
|
|
|
# @Time : 2025/01/19 01:00
|
|
|
|
|
# @UpdateTime : 2025/01/19 01:00
|
|
|
|
|
# @Author : sonder
|
|
|
|
|
# @File : login.py
|
|
|
|
|
# @Software : PyCharm
|
|
|
|
|
# @Comment : 本程序
|
2025-03-31 04:25:09 +08:00
|
|
|
|
import json
|
2025-02-13 02:27:44 +08:00
|
|
|
|
import uuid
|
|
|
|
|
from datetime import timedelta, datetime
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Request, Depends
|
|
|
|
|
from fastapi.encoders import jsonable_encoder
|
|
|
|
|
from starlette.responses import JSONResponse
|
|
|
|
|
from tortoise.expressions import Q
|
|
|
|
|
|
|
|
|
|
from annotation.log import Log
|
|
|
|
|
from config.constant import BusinessType
|
|
|
|
|
from config.constant import RedisKeyConfig
|
|
|
|
|
from controller.login import CustomOAuth2PasswordRequestForm, LoginController
|
|
|
|
|
from controller.query import QueryController
|
2025-03-31 04:25:09 +08:00
|
|
|
|
from models import Department, User, Role, UserRole, LoginLog, OperationLog, QueryCodeLog, QueryCode, HtsClass, HtsItem,Version
|
2025-02-13 02:27:44 +08:00
|
|
|
|
from schemas.common import BaseResponse
|
|
|
|
|
from schemas.login import LoginParams, GetUserInfoResponse, LoginResponse, GetCaptchaResponse, GetEmailCodeParams, \
|
|
|
|
|
ResetPasswordParams
|
|
|
|
|
from schemas.user import RegisterUserParams
|
|
|
|
|
from utils.captcha import Captcha
|
|
|
|
|
from utils.log import logger
|
|
|
|
|
from utils.mail import Email
|
|
|
|
|
from utils.password import Password
|
|
|
|
|
from utils.response import Response
|
|
|
|
|
|
|
|
|
|
loginAPI = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.post("/login", response_class=JSONResponse, summary="用户登录")
|
|
|
|
|
@Log(title="用户登录", business_type=BusinessType.GRANT, log_type="login")
|
|
|
|
|
async def login(
|
|
|
|
|
request: Request,
|
|
|
|
|
params: CustomOAuth2PasswordRequestForm = Depends()
|
|
|
|
|
):
|
2025-02-19 01:21:14 +08:00
|
|
|
|
request.app.state.session_id = None
|
|
|
|
|
request.app.state.login_status = False
|
2025-02-13 02:27:44 +08:00
|
|
|
|
user = LoginParams(
|
|
|
|
|
username=params.username,
|
|
|
|
|
password=params.password,
|
|
|
|
|
loginDays=params.loginDays,
|
|
|
|
|
code=params.code,
|
|
|
|
|
uuid=params.uuid
|
|
|
|
|
)
|
|
|
|
|
captcha_enabled = (
|
|
|
|
|
True
|
2025-02-19 01:21:14 +08:00
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_captcha_enabled')
|
2025-02-13 02:27:44 +08:00
|
|
|
|
== 'true'
|
|
|
|
|
else False
|
|
|
|
|
)
|
|
|
|
|
# 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
|
|
|
|
|
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get(
|
|
|
|
|
'referer') else False
|
|
|
|
|
request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get(
|
|
|
|
|
'referer') else False
|
|
|
|
|
# 验证码校验,如果开启验证码校验,则进行验证码校验,如果关闭则跳过验证码校验. 如果请求来自api文档,则跳过验证码校验
|
|
|
|
|
if captcha_enabled and not request_from_redoc and not request_from_swagger:
|
|
|
|
|
result = await Captcha.verify_code(request, code=user.code, session_id=user.uuid)
|
|
|
|
|
if not result["status"]:
|
|
|
|
|
return Response.error(msg=result["msg"])
|
|
|
|
|
result = await LoginController.login(user)
|
|
|
|
|
if result["status"]:
|
|
|
|
|
await request.app.state.redis.set(
|
|
|
|
|
f'{RedisKeyConfig.ACCESS_TOKEN.key}:{result["session_id"]}',
|
|
|
|
|
result["accessToken"],
|
|
|
|
|
ex=timedelta(minutes=result["expiresIn"]),
|
|
|
|
|
)
|
|
|
|
|
userInfo = str(jsonable_encoder(result["userInfo"]))
|
|
|
|
|
await request.app.state.redis.set(
|
|
|
|
|
f'{RedisKeyConfig.USER_INFO.key}:{result["userInfo"]["id"]}',
|
|
|
|
|
userInfo,
|
|
|
|
|
ex=timedelta(minutes=5),
|
|
|
|
|
)
|
|
|
|
|
request.app.state.session_id = result["session_id"]
|
2025-02-19 01:21:14 +08:00
|
|
|
|
request.app.state.login_status = True
|
2025-02-13 02:27:44 +08:00
|
|
|
|
if request_from_swagger or request_from_redoc:
|
|
|
|
|
return {'access_token': result["accessToken"], 'token_type': 'Bearer',
|
|
|
|
|
"expires_in": result["expiresIn"] * 60}
|
|
|
|
|
result.pop("status")
|
|
|
|
|
result.pop("expiresIn")
|
|
|
|
|
result.pop("session_id")
|
|
|
|
|
result.pop("userInfo")
|
|
|
|
|
return Response.success(data=result)
|
2025-02-19 01:21:14 +08:00
|
|
|
|
request.app.state.login_status = False
|
2025-02-13 02:27:44 +08:00
|
|
|
|
return Response.failure(msg="登录失败,账号或密码错误!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.post("/register", response_class=JSONResponse, response_model=LoginResponse, summary="用户注册")
|
|
|
|
|
async def register(request: Request, params: RegisterUserParams):
|
|
|
|
|
register_enabled = (
|
|
|
|
|
True
|
2025-02-23 22:03:45 +08:00
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_register_enabled')
|
2025-02-13 02:27:44 +08:00
|
|
|
|
== 'true'
|
|
|
|
|
else False
|
|
|
|
|
)
|
|
|
|
|
if not register_enabled:
|
|
|
|
|
return Response.error(msg="注册功能已关闭!")
|
|
|
|
|
result = await Email.verify_code(request, username=params.username, mail=params.email, code=params.code)
|
|
|
|
|
if not result["status"]:
|
|
|
|
|
return Response.error(msg=result["msg"])
|
|
|
|
|
if await QueryController.register_user_before(username=params.username, phone=params.phone, email=params.email):
|
|
|
|
|
return Response.error(msg="注册失败,用户已存在!")
|
|
|
|
|
params.password = await Password.get_password_hash(input_password=params.password)
|
2025-02-23 22:03:45 +08:00
|
|
|
|
# 默认分配注册用户
|
|
|
|
|
userRole = await Role.get_or_none(department__name="注册用户", code="user", del_flag=1).values(
|
|
|
|
|
department_id="department__id", id="id")
|
|
|
|
|
if not params.department_id:
|
|
|
|
|
params.department_id = userRole.get("department_id", "")
|
2025-02-13 02:27:44 +08:00
|
|
|
|
department = await Department.get_or_none(id=params.department_id)
|
2025-02-23 22:03:45 +08:00
|
|
|
|
userRole = await Role.get_or_none(department__id=department.id, code="user", del_flag=1).values(id="id")
|
|
|
|
|
print(userRole)
|
2025-02-13 02:27:44 +08:00
|
|
|
|
user = await User.create(
|
|
|
|
|
username=params.username,
|
|
|
|
|
password=params.password,
|
|
|
|
|
nickname=params.nickname,
|
|
|
|
|
phone=params.phone,
|
|
|
|
|
email=params.email,
|
|
|
|
|
gender=params.gender,
|
|
|
|
|
department=department,
|
|
|
|
|
status=params.status,
|
|
|
|
|
)
|
|
|
|
|
if user:
|
2025-02-23 22:03:45 +08:00
|
|
|
|
# 默认分配普通用户角色
|
|
|
|
|
await UserRole.create(
|
|
|
|
|
user_id=user.id,
|
|
|
|
|
role_id=userRole.get("id", ""),
|
|
|
|
|
)
|
2025-02-13 02:27:44 +08:00
|
|
|
|
userParams = LoginParams(
|
|
|
|
|
username=params.username,
|
|
|
|
|
password=params.password
|
|
|
|
|
)
|
|
|
|
|
result = await LoginController.login(userParams)
|
|
|
|
|
if result["status"]:
|
|
|
|
|
await request.app.state.redis.set(
|
|
|
|
|
f'{RedisKeyConfig.ACCESS_TOKEN.key}:{result["session_id"]}',
|
|
|
|
|
result["accessToken"],
|
|
|
|
|
ex=timedelta(minutes=result["expiresIn"]),
|
|
|
|
|
)
|
|
|
|
|
userInfo = str(jsonable_encoder(result["userInfo"]))
|
|
|
|
|
await request.app.state.redis.set(
|
|
|
|
|
f'{RedisKeyConfig.USER_INFO.key}:{result["userInfo"]["id"]}',
|
|
|
|
|
userInfo,
|
|
|
|
|
ex=timedelta(minutes=5),
|
|
|
|
|
)
|
|
|
|
|
result.pop("status")
|
|
|
|
|
result.pop("expiresIn")
|
|
|
|
|
result.pop("session_id")
|
|
|
|
|
result.pop("userInfo")
|
|
|
|
|
return Response.success(msg="注册成功!", data=result)
|
2025-02-23 22:03:45 +08:00
|
|
|
|
return Response.success(msg="注册成功!")
|
2025-02-13 02:27:44 +08:00
|
|
|
|
else:
|
|
|
|
|
return Response.error(msg="注册失败!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.get("/captcha", response_class=JSONResponse, response_model=GetCaptchaResponse, summary="获取验证码")
|
|
|
|
|
async def get_captcha(request: Request):
|
|
|
|
|
captcha_enabled = (
|
|
|
|
|
True
|
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_captcha_enabled')
|
|
|
|
|
== 'true'
|
|
|
|
|
else False
|
|
|
|
|
)
|
|
|
|
|
register_enabled = (
|
|
|
|
|
True
|
2025-02-13 18:04:42 +08:00
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_register_enabled')
|
2025-02-13 02:27:44 +08:00
|
|
|
|
== 'true'
|
|
|
|
|
else False
|
|
|
|
|
)
|
|
|
|
|
if captcha_enabled:
|
|
|
|
|
captcha_type = (
|
|
|
|
|
await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_captcha_type')
|
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.SYSTEM_CONFIG.key}:account_captcha_type')
|
|
|
|
|
else "1"
|
|
|
|
|
)
|
|
|
|
|
captcha_result = await Captcha.create_captcha(captcha_type)
|
|
|
|
|
session_id = str(uuid.uuid4())
|
|
|
|
|
captcha = captcha_result[0]
|
|
|
|
|
result = captcha_result[-1]
|
|
|
|
|
await request.app.state.redis.set(
|
|
|
|
|
f'{RedisKeyConfig.CAPTCHA_CODES.key}:{session_id}', result, ex=timedelta(minutes=2)
|
|
|
|
|
)
|
|
|
|
|
logger.info(f'编号为{session_id}的会话获取图片验证码成功')
|
|
|
|
|
|
|
|
|
|
return Response.success(data={
|
|
|
|
|
"uuid": session_id,
|
|
|
|
|
"captcha": captcha,
|
|
|
|
|
"captcha_enabled": captcha_enabled,
|
|
|
|
|
"register_enabled": register_enabled,
|
|
|
|
|
})
|
|
|
|
|
else:
|
|
|
|
|
return Response.success(data={
|
|
|
|
|
"uuid": None,
|
|
|
|
|
"captcha": None,
|
|
|
|
|
"captcha_enabled": captcha_enabled,
|
|
|
|
|
"register_enabled": register_enabled,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.post("/code", response_class=JSONResponse, response_model=BaseResponse, summary="获取邮件验证码")
|
|
|
|
|
async def get_code(request: Request, params: GetEmailCodeParams):
|
|
|
|
|
result = await Email.send_email(request, username=params.username, title=params.title, mail=params.mail)
|
|
|
|
|
if result:
|
|
|
|
|
return Response.success(msg="验证码发送成功!")
|
|
|
|
|
return Response.error(msg="验证码发送失败!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.put("/resetPassword", response_class=JSONResponse, response_model=BaseResponse, summary="重置密码")
|
|
|
|
|
@loginAPI.post("/resetPassword", response_class=JSONResponse, response_model=BaseResponse, summary="重置密码")
|
|
|
|
|
async def reset_password(request: Request, params: ResetPasswordParams):
|
|
|
|
|
result = await Email.verify_code(request, username=params.username, mail=params.mail, code=params.code)
|
|
|
|
|
if not result["status"]:
|
|
|
|
|
return Response.error(msg=result["msg"])
|
|
|
|
|
user = await User.get_or_none(Q(username=params.username) | Q(phone=params.username), email=params.mail)
|
|
|
|
|
if user:
|
|
|
|
|
user.password = await Password.get_password_hash(input_password=params.password)
|
|
|
|
|
await user.save()
|
|
|
|
|
return Response.success(msg="密码重置成功!")
|
|
|
|
|
return Response.error(msg="密码重置失败,用户不存在!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.get("/info", response_class=JSONResponse, response_model=GetUserInfoResponse, summary="获取用户信息")
|
|
|
|
|
@Log(title="获取用户信息", business_type=BusinessType.SELECT)
|
|
|
|
|
async def info(
|
|
|
|
|
request: Request,
|
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user)
|
|
|
|
|
):
|
|
|
|
|
return Response.success(data=current_user)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.get("/getRoutes", response_class=JSONResponse, summary="获取路由信息")
|
2025-02-23 22:03:45 +08:00
|
|
|
|
@Log(title="获取路由信息", business_type=BusinessType.SELECT)
|
2025-02-13 02:27:44 +08:00
|
|
|
|
async def get_routes(request: Request, current_user: dict = Depends(LoginController.get_current_user)):
|
2025-02-23 22:03:45 +08:00
|
|
|
|
sub_departments = current_user.get("sub_departments")
|
2025-02-13 02:27:44 +08:00
|
|
|
|
routes = await request.app.state.redis.get(f'{RedisKeyConfig.USER_ROUTES.key}:{current_user["id"]}')
|
|
|
|
|
if routes:
|
|
|
|
|
return Response.success(data=eval(routes))
|
2025-02-23 22:03:45 +08:00
|
|
|
|
routes = await LoginController.get_user_routes(current_user["id"], sub_departments=sub_departments)
|
2025-02-13 02:27:44 +08:00
|
|
|
|
userRoutes = str(jsonable_encoder(routes))
|
|
|
|
|
await request.app.state.redis.set(
|
|
|
|
|
f'{RedisKeyConfig.USER_ROUTES.key}:{current_user["id"]}',
|
|
|
|
|
userRoutes,
|
|
|
|
|
ex=timedelta(minutes=5),
|
|
|
|
|
)
|
|
|
|
|
return Response.success(data=routes)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.post("/refreshToken", response_class=JSONResponse, response_model=LoginResponse, summary="刷新token")
|
|
|
|
|
@Log(title="刷新token", business_type=BusinessType.GRANT)
|
|
|
|
|
async def refresh_token(request: Request,
|
|
|
|
|
current_user: dict = Depends(LoginController.get_current_user)
|
|
|
|
|
):
|
|
|
|
|
session_id = uuid.uuid4().__str__()
|
|
|
|
|
accessToken = await LoginController.create_token(
|
|
|
|
|
data={"user": current_user, "id": current_user.get("id"), "session_id": session_id},
|
|
|
|
|
expires_delta=timedelta(minutes=2 * 24 * 60))
|
|
|
|
|
expiresTime = (datetime.now() + timedelta(minutes=2 * 24 * 60)).timestamp()
|
|
|
|
|
refreshToken = await LoginController.create_token(
|
|
|
|
|
data={"user": current_user, "id": current_user.get("id"), "session_id": session_id},
|
|
|
|
|
expires_delta=timedelta(minutes=(4 * 24 + 2) * 60))
|
|
|
|
|
return Response.success(data={"accessToken": accessToken, "refreshToken": refreshToken, "expiresTime": expiresTime})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.post("/logout", response_class=JSONResponse, response_model=BaseResponse, summary="用户登出")
|
|
|
|
|
@Log(title="退出登录", business_type=BusinessType.FORCE)
|
|
|
|
|
async def logout(request: Request, status: bool = Depends(LoginController.logout)):
|
|
|
|
|
if status:
|
|
|
|
|
return Response.success(data="退出成功!")
|
|
|
|
|
return Response.error(data="登出失败!")
|
2025-02-26 17:07:16 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@loginAPI.post("/unsubscribe", response_class=JSONResponse, response_model=BaseResponse, summary="用户注销")
|
|
|
|
|
@Log(title="用户注销", business_type=BusinessType.FORCE)
|
|
|
|
|
async def unsubscribe(request: Request, current_user: dict = Depends(LoginController.get_current_user)):
|
|
|
|
|
id = current_user.get("id")
|
|
|
|
|
session_id = current_user.get("session_id")
|
|
|
|
|
if user := await User.get_or_none(id=id, del_flag=1):
|
|
|
|
|
user.del_flag = 0
|
|
|
|
|
await user.save()
|
|
|
|
|
redis_token = await request.app.state.redis.get(f'{RedisKeyConfig.ACCESS_TOKEN.key}:{session_id}')
|
|
|
|
|
if redis_token:
|
|
|
|
|
await request.app.state.redis.delete(f'{RedisKeyConfig.ACCESS_TOKEN.key}:{session_id}')
|
|
|
|
|
# 移除用户角色
|
|
|
|
|
await UserRole.filter(user_id=user.id, del_flag=1).update(del_flag=0)
|
|
|
|
|
# 移除用户登录日志
|
|
|
|
|
await LoginLog.filter(user_id=user.id, del_flag=1).update(del_flag=0)
|
|
|
|
|
# 移除用户操作日志
|
|
|
|
|
await OperationLog.filter(user_id=user.id, del_flag=1).update(del_flag=0)
|
|
|
|
|
# 移除编码查询日志
|
|
|
|
|
logs = await QueryCodeLog.filter(operator__id=user.id, del_flag=1).values("id")
|
|
|
|
|
for log in logs:
|
|
|
|
|
await QueryCode.filter(session_id=log["id"], del_flag=1).update(del_flag=0)
|
|
|
|
|
await QueryCodeLog.filter(id=log["id"], del_flag=1).update(del_flag=0)
|
|
|
|
|
# 更新用户信息缓存
|
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.USER_INFO.key}:{id}'):
|
|
|
|
|
await request.app.state.redis.delete(f'{RedisKeyConfig.USER_INFO.key}:{id}')
|
|
|
|
|
# 更新用户路由缓存
|
|
|
|
|
if await request.app.state.redis.get(f'{RedisKeyConfig.USER_ROUTES.key}:{id}'):
|
|
|
|
|
await request.app.state.redis.delete(f'{RedisKeyConfig.USER_ROUTES.key}:{id}')
|
|
|
|
|
return Response.success(data="注销成功!")
|
|
|
|
|
else:
|
|
|
|
|
return Response.error(data="注销失败!")
|
2025-03-31 04:25:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# @loginAPI.get("/test")
|
|
|
|
|
# async def test(request: Request):
|
|
|
|
|
# values = await HtsClass.filter(del_flag=1).values("chapter_name","id")
|
|
|
|
|
# version=await Version.get(id="4b2101cc-9bc3-4084-af16-eeb27b24b682")
|
|
|
|
|
# for item in values:
|
|
|
|
|
# hts = str(item["chapter_name"]).replace("Chapter", "").strip()
|
|
|
|
|
# class_=await HtsClass.get(id=item["id"])
|
|
|
|
|
# if hts == "77":
|
|
|
|
|
# continue
|
|
|
|
|
#
|
|
|
|
|
# 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(f"E:/PythonCodes/cutoms-system-backend/test/{hts}.json", '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(item["chapter_name"]).replace("Chapter", "").strip() if int(
|
|
|
|
|
# str(item["chapter_name"]).replace("Chapter", "").strip(" ")) > 10 else "0" +
|
|
|
|
|
# str(item["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)
|
|
|
|
|
# print(class_.chapter_name)
|
|
|
|
|
|