编写客户端接口

This commit is contained in:
wcq 2023-09-07 12:03:46 +08:00
parent e3c95800da
commit 4e9972206c
19 changed files with 243 additions and 52 deletions

View File

@ -4,7 +4,7 @@ from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from mods.user.mods.user.models import Base
from main import common_db_base_v2
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
@ -13,12 +13,13 @@ config = context.config
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
config.set_main_option("sqlalchemy.url", 'postgresql://postgres:12345@localhost/postgres')
config.set_main_option("sqlalchemy.url", 'postgresql://postgres:12345@localhost/wd_smebiz_v2')
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = Base.metadata
from mods.router import *
target_metadata = common_db_base_v2.metadata
# other values from the config, defined by the needs of env.py,

16
docs_fetch_hook.js Normal file
View File

@ -0,0 +1,16 @@
if(!window.fetchOrg){
window.fetchOrg = window.fetch
}
var token ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImVjZDlmYTI3Yjc3YTQ1MTNhNjQxNjQ4OTVlNDFhOGY4IiwiY29tcGFueV9uYW1lIjoiXHU5MWNkXHU1ZTg2XHU4M2IxXHU3ZjhlXHU4MzZmXHU0ZTFhXHU4MGExXHU0ZWZkXHU2NzA5XHU5NjUwXHU1MTZjXHU1M2Y4IiwiY29tcGFueV9pZCI6IjkxMzEwMTAxMTMyNTA4MDkySyIsImVtYWlsIjoid3VjaHVucXVhbkBmZWNyLmNvbS5jbiIsInBob25lIjoiMTgwOTA0NzgxMjMiLCJleHAiOjE2OTQyMjMyMzJ9.K95pvx3V0vC6xGpV41ntGG70YWbXl-XNUuDtr9MIuP8"
var fetchHook = (...args) => {
if (args.length > 1) {
if (args[1]["headers"]) {
args[1]["headers"]["Authorization"] = token
} else {
args[1]["headers"] = { Authorization: token }
}
}
return window.fetchOrg(...args)
}
window.fetch = fetchHook

View File

@ -4,7 +4,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from starlette.staticfiles import StaticFiles
from config.log import uvicorn_log_config
from context.common import conf, common_db, common_db_v2
from context.common import conf, common_db, common_db_v2,common_db_base_v2
from mods import router as main_router
common_db.init_database(create_db=True)

View File

@ -11,7 +11,7 @@ from ...common import get_db
router = APIRouter(tags=["企业用户接口"], prefix='/company_user')
@router.post('/company_user/login_by_email', summary='邮箱登录',
@router.post('/login_by_email', summary='用户邮箱登录接口',
response_model=schemas.LoginByEmailRes)
def login_by_email(req: schemas.LoginByEmailReq,
db: Session = Depends(get_db)):
@ -27,7 +27,21 @@ def login_by_email(req: schemas.LoginByEmailReq,
return schemas.LoginByEmailRes(token=token)
@router.post('/company_user/login_by_passwd', summary='邮箱密码登录',
@router.post('/refresh_token', summary='用户token刷新接口',
response_model=schemas.RefreshTokenRes)
def login_by_email(db: Session = Depends(get_db),
token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend),
):
user = db.query(CompanyUser).filter(CompanyUser.id == token_data.id).first()
if not user:
raise HTTPException(detail="该账户未注册", status_code=303)
user_data = user.to_dict()
token_data = company_user_auth_util.tokenDataModel(**user_data)
token = company_user_auth_util.create_token(token_data)
return schemas.LoginByEmailRes(token=token)
@router.post('/login_by_passwd', summary='用户邮箱密码登录接口',
response_model=schemas.LoginByPasswdRes)
def login_by_passwd(req: schemas.LoginByPasswdReq,
db: Session = Depends(get_db)):
@ -45,7 +59,7 @@ def login_by_passwd(req: schemas.LoginByPasswdReq,
return schemas.LoginByEmailRes(token=token)
@router.post("/company_user/get_email_login_verify_code", summary="获取登录邮箱验证码")
@router.post("/get_email_login_verify_code", summary="用户邮箱登录验证码获取接口")
def get_email_login_verify_code(body: schemas.GetEmailVerifyCode):
try:
email = body.email
@ -101,7 +115,7 @@ def get_email_login_verify_code(body: schemas.GetEmailVerifyCode):
# return {'msg': "注册成功", 'state': 1}
@router.post('/company_user/get_user_info', summary='获取用户信息', response_model=schemas.GetUserInfoRes)
@router.post('/get_user_info', summary='用户信息获取接口', response_model=schemas.GetUserInfoRes)
def get_user_info(token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend),
db: Session = Depends(get_db)):
user = db.query(CompanyUser).filter(token_data.id == CompanyUser.id).first()

View File

@ -21,6 +21,10 @@ class LoginByEmailRes(BaseModel):
token: str
class RefreshTokenRes(BaseModel):
token: str
class GetEmailVerifyCode(BaseModel):
email: str

View File

@ -6,28 +6,32 @@ from context.common import CompanyUserTokenDataModel, company_user_auth_util
from utils.common_utils import file_md5
from utils.print_utils import print_error
from . import schemas
from ...models import UploadFile, RateServe, CompanyUser
from ...models import UploadFile, RateServe, CompanyUser, RatePostDataCheckNode, RateDataPreparationNode, RateWorkNode, \
ReportWorkNode, RateServeStatusLog, CertWorkNode
from ...common import get_db
from ...schemas import RateServeState, NodeState
from ...utils.rate_utils import sheet_parse
from ...mods.upload_file.crud import file_upload, types_dic_rev
from ...mods.rate_serve import crud as rate_serve_crud
from ...mods.rate_serve_status_log import crud as rate_serve_status_log_crud
router = APIRouter(tags=["评级服务"])
@router.post('/rate_post_data_check_node/rate_sheet_upload', response_model=schemas.RateSheetUploadRes,
summary='问卷上传')
summary='评级服务问卷上传接口')
def func(file: FastApiUploadFile = File(...),
db: Session = Depends(get_db),
# token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend)
token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend)
):
if types_dic_rev.get(file.content_type) != 'xlsx':
raise HTTPException(status_code=303, detail='请上传.xlsx文件')
new_item = file_upload(file, db, sub_type='填报问卷')
new_item = file_upload(file, db, sub_type='填报问卷', user_id=token_data.id)
return new_item.to_dict()
@router.post('/rate_serve/apply', summary='评级服务申请', response_model=schemas.RateServeApplyRes)
@router.post('/rate_serve/apply', summary='评级服务申请接口', response_model=schemas.RateServeApplyRes)
def func(req: schemas.RateServeApplyReq,
token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend),
db: Session = Depends(get_db)):
@ -38,15 +42,60 @@ def func(req: schemas.RateServeApplyReq,
if not sheet_file:
raise HTTPException(status_code=303, detail='未上传文件或文件错误,请重新上传')
try:
wb_data = sheet_parse(sheet_file.path)
# 文件路径需要去掉第一个'/'
file_path = sheet_file.path[1:]
wb_data = sheet_parse(file_path)
except Exception as e:
print_error(e)
raise HTTPException(status_code=303, detail='分析问卷失败,请在原问卷上按规范填报')
# 评级服务申请创建接口
item = RateServe()
user=db.query(CompanyUser).filter(CompanyUser.id==token_data.id).first()
user = db.query(CompanyUser).filter(CompanyUser.id == token_data.id).first()
if not user:
raise HTTPException(status_code=303,detail="用户不存在")
raise HTTPException(status_code=303, detail="用户不存在")
if not user.company_id:
raise HTTPException(status_code=303,detail="用户未绑定公司")
raise HTTPException(status_code=303, detail="用户未绑定公司")
item.company_id = user.company_id
item.serve_id
item.serve_id = rate_serve_crud.make_rate_serve_id(db)
item.apply_user_id = user.id
item.project = '评级服务'
item.status = RateServeState.examining
item.rate_post_data_check_node = RatePostDataCheckNode(file_id=req.file_id, parsed_data=wb_data, data=wb_data,
status=NodeState.incomplete)
item.rate_data_preparation_node = RateDataPreparationNode(status=NodeState.incomplete)
item.rate_work_node = RateWorkNode(status=NodeState.incomplete)
item.report_work_node = ReportWorkNode(status=NodeState.incomplete)
item.cert_work_node = CertWorkNode(status=NodeState.incomplete)
db.add(item)
db.commit()
db.refresh(item)
rate_serve_status_log_crud.add_rate_serve_status_log(db, serve_id=item.serve_id, detail="评级申请成功,待审核",
status=RateServeState.examining)
return schemas.RateServeApplyRes(serve_id=item.serve_id)
@router.post('/rate_serve/query', summary='评价服务信息筛选接口', response_model=schemas.RateServeQueryRes)
def func(req: schemas.RateServeQueryReq,
token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend),
db: Session = Depends(get_db)):
req.query.company_id = token_data.company_id
count, query = rate_serve_crud.rate_serve_query(db, req)
items = [schemas.RateServeInfoLite(**item.to_full_dict(include=req.include,
ex_include=req.ex_include,
relation_use_id=req.relation_use_id), apply_user=None) for
item in query]
return schemas.RateServeQueryRes(count=count, items=items)
@router.post('/rate_serve/get', summary='评级服务信息获取接口', response_model=schemas.RateServeGetRes)
def func(req: schemas.RateServeGetReq,
token_data: CompanyUserTokenDataModel = Depends(company_user_auth_util.token_data_depend),
db: Session = Depends(get_db)):
item = db.query(RateServe).filter(
RateServe.company_id == token_data.company_id and RateServe.serve_id == req.serve_id).first()
if not item:
raise HTTPException(status_code=403, detail='未查询到数据')
return schemas.RateServeInfoLite(**item.to_full_dict())

View File

@ -1,14 +1,35 @@
from datetime import datetime
from typing import Optional
from typing import Optional, List
from pydantic import BaseModel
from ...mods.rate_serve.schemas import RateServeQuery, RateServeInfo, RateServeId, RateServeInfoLite
from ...schemas import NodeState
class RateServeApplyReq(BaseModel):
file_id: str
class RateServeApplyRes(BaseModel):
serve_id: str
class RateServeQueryReq(RateServeQuery):
pass
class RateServeQueryRes(BaseModel):
count: int
items: List[RateServeInfoLite]
pass
class RateServeGetReq(RateServeId):
pass
class RateServeGetRes(RateServeInfoLite):
pass

View File

@ -102,6 +102,8 @@ class RateServeStatusLog(Base, SalBase):
serve_id: Mapped[str] = mapped_column(ForeignKey("rate_serve.serve_id", ondelete='CASCADE', onupdate='CASCADE'),
comment="服务编号")
detail: Mapped[str] = mapped_column(TEXT, nullable=True, comment="状态明细说明")
status: Mapped[RateServeState] = mapped_column(Enum(RateServeState),
default=RateServeState.examining, comment="服务状态", nullable=True)
create_time: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=True,
comment='创建时间')
@ -129,6 +131,9 @@ class RatePostDataCheckNode(Base, SalBase):
comment='创建时间')
finish_time: Mapped[datetime] = mapped_column(DateTime, nullable=True, comment='完成时间')
def to_lite_dict(self):
return self.to_dict(ex_include=['data', 'parsed_data', 'serve_id', 'create_time'])
class RateDataPreparationNode(Base, SalBase):
"""
@ -147,6 +152,9 @@ class RateDataPreparationNode(Base, SalBase):
comment='创建时间')
finish_time: Mapped[datetime] = mapped_column(DateTime, nullable=True, comment='完成时间')
def to_lite_dict(self):
return self.to_dict(ex_include=['data', 'serve_id', 'create_time'])
class RateWorkNode(Base, SalBase):
"""
@ -168,6 +176,9 @@ class RateWorkNode(Base, SalBase):
comment='创建时间')
finish_time: Mapped[datetime] = mapped_column(DateTime, nullable=True, comment='完成时间')
def to_lite_dict(self):
return self.to_dict(ex_include=['data', 'serve_id', 'create_time'])
class ReportWorkNode(Base, SalBase):
"""
@ -195,6 +206,9 @@ class ReportWorkNode(Base, SalBase):
comment='创建时间')
finish_time: Mapped[datetime] = mapped_column(DateTime, nullable=True, comment='完成时间')
def to_lite_dict(self):
return self.to_dict(ex_include=['serve_id', 'create_time'])
class CertWorkNode(Base, SalBase):
"""
@ -214,6 +228,9 @@ class CertWorkNode(Base, SalBase):
comment='创建时间')
finish_time: Mapped[datetime] = mapped_column(DateTime, nullable=True, comment='完成时间')
def to_lite_dict(self):
return self.to_dict()
# # 创建 SQLite 数据库引擎
# DATABASE_URL = "sqlite:///example.db"
# engine = create_engine(DATABASE_URL, echo=True)

View File

@ -0,0 +1,15 @@
# 评级节点的流程顺序,数组即数组内的节点没有先后顺序
rate_flow_nodes = [
"rate_post_data_check_node",
"rate_data_preparation_node",
"rate_work_node",
"report_work_node",
"cert_work_node"
]
rate_flow_index = [
"rate_post_data_check_node",
"rate_data_preparation_node",
"rate_work_node",
["report_work_node", "cert_work_node"]
]

View File

@ -3,6 +3,7 @@ from datetime import datetime
from random import choices
from typing import List
from fastapi import HTTPException
from sqlalchemy.orm import Session
from utils.sqlalchemy_common_utils import ModelCRUD
@ -39,5 +40,13 @@ def rate_serve_all(db: Session):
def make_rate_serve_id(db: Session):
"""
创建服务ID最大检测两次重复就抛出异常
"""
time_str = datetime.now().strftime('%y%m%H%S%f')
return f"XP{time_str}{choices(string.ascii_uppercase, k=5)}"
new_id = f"XP{time_str}{choices(string.ascii_uppercase, k=5)}"
if db.query(RateServe).filter(RateServe.serve_id == new_id).first():
new_id = f"XP{time_str}{choices(string.ascii_uppercase, k=5)}"
if db.query(RateServe).filter(RateServe.serve_id == new_id).first():
raise HTTPException(status_code=303, detail='创建服务ID失败')
return f"XP{time_str}{''.join(choices(string.ascii_uppercase, k=5))}"

View File

@ -1,6 +1,7 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from .config import rate_flow_nodes
from ...common import get_db
from . import crud
from . import schemas
@ -31,17 +32,21 @@ def rate_serve_get(req: schemas.RateServeGetReq, db: Session = Depends(get_db)):
item = crud.rate_serve_get(db, req)
if not item:
raise HTTPException(detail="未查询到信息", status_code=404)
return schemas.RateServeGetRes(**item.to_full_dict())
data = item.to_dict()
for key in rate_flow_nodes:
node = getattr(item, key)
if node:
# 每个节点有一个to_lite_dict方法
data[key] = node.to_lite_dict()
return schemas.RateServeGetRes(**data)
@router.post("/query", summary="查询评级服务", response_model=schemas.RateServeQueryRes)
def rate_serve_query(req: schemas.RateServeQueryReq, db: Session = Depends(get_db)):
count, query = crud.rate_serve_query(db, req)
items = [schemas.RateServeInfo(**item.to_full_dict(include=req.include,
ex_include=req.ex_include,
relation_use_id=req.relation_use_id)) for item in query]
ex_include=req.ex_include,
relation_use_id=req.relation_use_id)) for item in query]
return schemas.RateServeQueryRes(count=count, items=items)
#########

View File

@ -3,15 +3,15 @@ from pydantic import BaseModel
from utils.pydantic_utils import AllOptional
from utils.sqlalchemy_common_utils import QueryParams, QueryInclude
from datetime import date, datetime
from ...schemas import RateServeState
from ...schemas import RateServeState, NodeState
class RateServeId(BaseModel):
serve_id: str
class RateServeAdd(BaseModel):
apply_user: Optional[dict] = None
class RateServeAddWithoutNode(BaseModel):
# apply_user: Optional[dict] = None
apply_user_id: str
company_id: str
company: Optional[Dict] = None
@ -20,6 +20,14 @@ class RateServeAdd(BaseModel):
create_time: Optional[datetime] = None
finish_time: Optional[datetime] = None
status_logs: Optional[List[dict]] = None
class LiteNode(BaseModel):
remark: Optional[str] = None
status: Optional[NodeState] = None
class RateServeAdd(RateServeAddWithoutNode):
rate_post_data_check_node: Optional[dict] = None
rate_data_preparation_node: Optional[dict] = None
rate_work_node: Optional[dict] = None
@ -27,7 +35,23 @@ class RateServeAdd(BaseModel):
cert_work_node: Optional[dict] = None
class RateServeAddOptional(RateServeAdd, metaclass=AllOptional):
class RateServeAddLite(RateServeAddWithoutNode):
rate_post_data_check_node: Optional[LiteNode] = None
rate_data_preparation_node: Optional[LiteNode] = None
rate_work_node: Optional[LiteNode] = None
report_work_node: Optional[LiteNode] = None
cert_work_node: Optional[LiteNode] = None
class RateServeAddWithoutNodeOptional(RateServeAddWithoutNode, metaclass=AllOptional):
pass
class RateServeAddOptional(RateServeAddWithoutNodeOptional, RateServeAdd):
pass
class RateServeAddLiteOptional(RateServeAddWithoutNodeOptional, RateServeAddLite):
pass
@ -35,6 +59,10 @@ class RateServeInfo(RateServeId, RateServeAddOptional):
pass
class RateServeInfoLite(RateServeId, RateServeAddLiteOptional):
pass
class RateServeUpdate(RateServeId, RateServeAddOptional):
pass

View File

@ -13,6 +13,12 @@ def rate_serve_status_log_add(db: Session, data: schemas.RateServeStatusLogAdd)
return model_crud.add(db, data)
def add_rate_serve_status_log(db: Session, serve_id, detail, status):
item = RateServeStatusLog(serve_id=serve_id, detail=detail, status=status)
db.add(item)
db.commit()
def rate_serve_status_log_delete(db: Session, data: schemas.RateServeStatusLogId):
return model_crud.delete(db, data)
@ -21,7 +27,8 @@ def rate_serve_status_log_update(db: Session, data: schemas.RateServeStatusLogUp
return model_crud.update(db, data)
def rate_serve_status_log_query(db: Session, query_data: schemas.RateServeStatusLogQuery) -> (int, List[RateServeStatusLog]):
def rate_serve_status_log_query(db: Session, query_data: schemas.RateServeStatusLogQuery) -> (
int, List[RateServeStatusLog]):
count, query, page, page_size = model_crud.query(db, query_data)
query = query.offset((page - 1) * page_size).limit(page_size)
return count, query
@ -33,4 +40,3 @@ def rate_serve_status_log_get(db: Session, data: schemas.RateServeStatusLogId) -
def rate_serve_status_log_all(db: Session):
return db.query(RateServeStatusLog).all()

View File

@ -3,6 +3,7 @@ from pydantic import BaseModel
from utils.pydantic_utils import AllOptional
from utils.sqlalchemy_common_utils import QueryParams, QueryInclude
from datetime import date, datetime
from ...schemas import RateServeState
class RateServeStatusLogId(BaseModel):
@ -12,6 +13,7 @@ class RateServeStatusLogId(BaseModel):
class RateServeStatusLogAdd(BaseModel):
serve_id: str
detail: str
status: Optional[RateServeState] = None
create_time: Optional[datetime] = None

View File

@ -48,29 +48,29 @@ def file_upload(file: FastApiUploadFile, db: Session, user_id=None, sub_type='')
common_type, _ = file.content_type.split('/')
content_start = file.file.read(10240)
file_md = file_md5(content_start)
file_save_name=file_md + f".{file_type}"
save_path = Path(f"static/upload_files/{common_type}/{file_type}")
file_save_path = save_path / file_md
file_url = f'/static/upload_files/{common_type}/{file_type}/{file_md + f".{file_type}"}'
file_save_path = save_path / file_save_name
file_url = f'/static/upload_files/{common_type}/{file_type}/{file_save_name}'
if not os.path.exists(save_path):
os.makedirs(save_path)
exited_file = db.query(UploadFile).filter(UploadFile.path != file_url).first()
if not exited_file:
if not os.path.exists(file_save_path):
with open(file_save_path, 'wb') as f:
f.write(content_start)
while True:
content = file.file.read(10240)
if content:
f.write(content)
else:
break
# exited_file = db.query(UploadFile).filter(UploadFile.path != file_url).first()
if not os.path.exists(file_save_path):
with open(file_save_path, 'wb') as f:
f.write(content_start)
while True:
content = file.file.read(10240)
if content:
f.write(content)
else:
break
user_id = user_id
new_item = UploadFile()
new_item.name = file.filename
new_item.path = file_url
new_item.type = file_type
new_item.sub_type = sub_type
new_item.sub_type = user_id
new_item.user_id = user_id
db.add(new_item)
db.commit()
db.refresh(new_item)

View File

@ -1,10 +1,10 @@
from fastapi import APIRouter
from .user.router import router as user_router
from .smebiz_rate.router import router as smebiz_rate_router
# from .smebiz_rate.router import router as smebiz_rate_router
from .rate.router import router as rate_router
router = APIRouter(prefix="")
router.include_router(user_router)
router.include_router(rate_router)
router.include_router(smebiz_rate_router)
# router.include_router(smebiz_rate_router)

View File

@ -14,6 +14,7 @@ class CompanyUserInfo(BaseModel):
create_time: Optional[datetime] = None
class CompanyUserAddInfo(BaseModel):
email: Optional[str] = None
phone: Optional[str] = None

View File

@ -1,4 +1,4 @@
from context.common import auth_util,email_verify_code, phone_verify_code,common_db
from context.common import auth_util,email_verify_code, phone_verify_code,common_db,common_db_v2
get_db = common_db.get_db
Base = common_db.Base
get_db = common_db_v2.get_db
Base = common_db_v2.Base

View File

@ -252,9 +252,9 @@ def query_common_params_core(model, query: Query, params: List[QueryParam]):
def query_common_query_core(model, query: Query, query_data: BaseModel):
q_data = query_data.dict(exclude_unset=True)
q_data = query_data.model_dump(exclude_unset=True)
cols: Dict[str, TypeInfo] = model.model_config.cols
for key, value in q_data:
for key, value in q_data.items():
annotation = query_data.__annotations__[key]
if key in cols:
column: Column = getattr(model, key)
@ -582,6 +582,9 @@ def get_model_col_names(model: Type[DeclarativeMeta]):
class SalBase:
__model_config: ModelConfig = None
def __init__(self, **kw: Any):
pass
@classmethod
@property
def model_config(cls) -> ModelConfig: