一个根据前端传来信息生成excel并返回下载链接的接口实例

172 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

接口代码内容(routers/api.py)

from fastapi import APIRouter, BackgroundTasks, HTTPException, Request

from excel import run
from schemas import TableData
from tasks import do_sync
from utils import get_host

router = APIRouter()


class ValidationError(HTTPException):
    def __init__(self, detail: str, status_code: int = 400):
        super().__init__(status_code, detail=detail)


NAME_COL = {
    "customerNum": 1,
    "factoryNum": 2,
    "size": 3,
    "juan": 4,
    "autoJian": 5,
    "price": 6,
    "total": 7,
    "pack": 8,
    "maiTou": 9,
}


def _build_table_body(lines):
    row = 4
    body = []
    for auto_no, line in enumerate(lines, 1):
        row_data = [(row, 0, auto_no)]
        for k, v in line.dict().items():
            if v is None:
                continue
            col = NAME_COL[k]
            if k == "size":
                v = f"0.45*{v}米/卷"
            elif k == "juan":
                unit = 50 if v >= 50 else 30
                auto_jian = v // unit + bool(v % unit)
                cell = (row, NAME_COL["autoJian"], auto_jian)
                row_data.append(cell)
            row_data.append((row, col, v))
        body.append(row_data)
        row += 1
    return body


@router.post("/like")
async def gen_table_生成表格(
    request: Request, table: TableData, background_tasks: BackgroundTasks
):
    datas = _build_table_body(table.lines)
    fname = run(datas, order_num=table.orderNum)
    url = f"{get_host(request)}/{fname}"
    background_tasks.add_task(do_sync, table.lines)
    return {"url": url}

pyproject.toml内容如下:

[tool.poetry]
name = "sunquan"
version = "0.3.0"
description = "Easy Order"
authors = ["Waket Zheng <waketzheng@gmail.com>"]

[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.75.2"
uvicorn = "^0.17.6"
xlrd = "^2.0.1"
xlwt = "^1.3.0"
python-dotenv = "^0.20.0"
openpyxl = "^3.0.9"
aioredis = {extras = ["hiredis"], version = "^2.0.1"}
httpx = "^0.22.0"
gunicorn = "^20.1.0"
loguru = "^0.6.0"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
bump="run:bump"
update="run:update"

run.py内容如下:

#!/usr/bin/env python
import os
import re
import shlex
import subprocess
import sys


def get_part(s: str) -> str:
    choices = {"1": "patch", "2": "minor", "3": "major"}
    choices.update({v: v for v in choices.values()})
    try:
        return choices[s]
    except KeyError:
        print(f"Invalid part: {s!r}")
        sys.exit(1)


def run_and_echo(cmd: str) -> int:
    print("-->", cmd)
    return os.system(cmd)


def bump():
    print("Current version:")
    os.system("poetry version")
    if sys.argv[1:]:
        part = get_part(sys.argv[1])
    else:
        tip = "Choices:\n1. patch\n2. minor\n3. major\n\nWhich one to bump?(Leave blank to use `patch`) "
        if a := input(tip).strip():
            part = get_part(a)
        else:
            part = "patch"
    if (rc := run_and_echo(f"bumpversion {part}")) == 0:
        sys.exit(run_and_echo("git push && git push --tags && git log -1"))
    sys.exit(rc)


def update():
    """升级所有依赖包到最新版"""
    cmd = "poetry show --tree"
    r = subprocess.run(shlex.split(cmd), capture_output=True)
    packages = re.findall(r"^([\w-]+)", r.stdout.decode(), re.M)
    upgrade = "poetry add " + " ".join(f"{i}@latest" for i in packages)
    print("-->", upgrade)
    subprocess.run(upgrade, shell=True)

utils.py内容如下:

from decimal import ROUND_HALF_UP, Decimal
from typing import Union

from settings import IS_PROD, PORT


def to_decimal(
    amount: Union[int, str, float, Decimal], decimal_places: int = 2
) -> Decimal:
    """将传入的数值,四舍五入转化为两个小数的Decimal对象
    :param decimal_places: 要保留多少为小数

    Usage::
        >>> to_decimal(1.565)
        Decimal('1.57')
        >>> to_decimal('1.564')
        Decimal('1.56')
        >>> to_decimal('1.564', 1)
        Decimal('1.6')
    """
    return Decimal(str(amount)).quantize(
        Decimal("0." + "0" * decimal_places), ROUND_HALF_UP
    )


def get_host(req) -> str:
    """获取协议+域名"""
    if v := getattr(req, "headers", {}).get("host"):
        if ":" in v:
            v = "http://" + v
        else:
            v = "https://" + v
        return v
    if IS_PROD:
        return "https://www.my-domain.com"  # 生产服
    return f"http://loalhost:{PORT}"  # 测试服