first commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .v1 import v1_router
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(v1_router, prefix="/v1")
|
||||
|
||||
|
||||
__all__ = ["api_router"]
|
||||
@@ -0,0 +1,29 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.core.dependency import DependPermisson, DependWeixinUser
|
||||
|
||||
from .apis import apis_router
|
||||
from .auditlog import auditlog_router
|
||||
from .base import base_router
|
||||
from .depts import depts_router
|
||||
from .menus import menus_router
|
||||
from .roles import roles_router
|
||||
from .users import users_router
|
||||
from .weixin import weixin_router
|
||||
from .finance_parse import finance_parse_router
|
||||
from .msg import msg_route_router
|
||||
from .automation import automation_router
|
||||
|
||||
v1_router = APIRouter()
|
||||
|
||||
v1_router.include_router(base_router, prefix="/base")
|
||||
v1_router.include_router(users_router, prefix="/user", dependencies=[DependPermisson])
|
||||
v1_router.include_router(roles_router, prefix="/role", dependencies=[DependPermisson])
|
||||
v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependPermisson])
|
||||
v1_router.include_router(apis_router, prefix="/api", dependencies=[DependPermisson])
|
||||
v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependPermisson])
|
||||
v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependPermisson])
|
||||
v1_router.include_router(finance_parse_router, prefix="/parse_finance_data", dependencies=[DependPermisson])
|
||||
v1_router.include_router(weixin_router, prefix='/weixin')
|
||||
v1_router.include_router(automation_router, prefix='/auto')
|
||||
v1_router.include_router(msg_route_router, dependencies=[DependWeixinUser])
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .apis import router
|
||||
|
||||
apis_router = APIRouter()
|
||||
apis_router.include_router(router, tags=["API模块"])
|
||||
|
||||
__all__ = ["apis_router"]
|
||||
@@ -0,0 +1,67 @@
|
||||
from fastapi import APIRouter, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.api import api_controller
|
||||
from app.schemas import Success, SuccessExtra
|
||||
from app.schemas.apis import *
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看API列表")
|
||||
async def list_api(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
path: str = Query(None, description="API路径"),
|
||||
summary: str = Query(None, description="API简介"),
|
||||
tags: str = Query(None, description="API模块"),
|
||||
):
|
||||
q = Q()
|
||||
if path:
|
||||
q &= Q(path__contains=path)
|
||||
if summary:
|
||||
q &= Q(summary__contains=summary)
|
||||
if tags:
|
||||
q &= Q(tags__contains=tags)
|
||||
total, api_objs = await api_controller.list(page=page, page_size=page_size, search=q, order=["tags", "id"])
|
||||
data = [await obj.to_dict() for obj in api_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看Api")
|
||||
async def get_api(
|
||||
id: int = Query(..., description="Api"),
|
||||
):
|
||||
api_obj = await api_controller.get(id=id)
|
||||
data = await api_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建Api")
|
||||
async def create_api(
|
||||
api_in: ApiCreate,
|
||||
):
|
||||
await api_controller.create(obj_in=api_in)
|
||||
return Success(msg="Created Successfully")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新Api")
|
||||
async def update_api(
|
||||
api_in: ApiUpdate,
|
||||
):
|
||||
await api_controller.update(id=api_in.id, obj_in=api_in)
|
||||
return Success(msg="Update Successfully")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除Api")
|
||||
async def delete_api(
|
||||
api_id: int = Query(..., description="ApiID"),
|
||||
):
|
||||
await api_controller.remove(id=api_id)
|
||||
return Success(msg="Deleted Success")
|
||||
|
||||
|
||||
@router.post("/refresh", summary="刷新API列表")
|
||||
async def refresh_api():
|
||||
await api_controller.refresh_api()
|
||||
return Success(msg="OK")
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .auditlog import router
|
||||
|
||||
auditlog_router = APIRouter()
|
||||
auditlog_router.include_router(router, tags=["审计日志模块"])
|
||||
|
||||
__all__ = ["auditlog_router"]
|
||||
@@ -0,0 +1,44 @@
|
||||
from fastapi import APIRouter, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.models.admin import AuditLog
|
||||
from app.schemas import SuccessExtra
|
||||
from app.schemas.apis import *
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看操作日志")
|
||||
async def get_audit_log_list(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
username: str = Query("", description="操作人名称"),
|
||||
module: str = Query("", description="功能模块"),
|
||||
method: str = Query("", description="请求方法"),
|
||||
summary: str = Query("", description="接口描述"),
|
||||
status: int = Query(None, description="状态码"),
|
||||
start_time: str = Query("", description="开始时间"),
|
||||
end_time: str = Query("", description="结束时间"),
|
||||
):
|
||||
q = Q()
|
||||
if username:
|
||||
q &= Q(username__icontains=username)
|
||||
if module:
|
||||
q &= Q(module__icontains=module)
|
||||
if method:
|
||||
q &= Q(method__icontains=method)
|
||||
if summary:
|
||||
q &= Q(summary__icontains=summary)
|
||||
if status:
|
||||
q &= Q(status=status)
|
||||
if start_time and end_time:
|
||||
q &= Q(created_at__range=[start_time, end_time])
|
||||
elif start_time:
|
||||
q &= Q(created_at__gte=start_time)
|
||||
elif end_time:
|
||||
q &= Q(created_at__lte=end_time)
|
||||
|
||||
audit_log_objs = await AuditLog.filter(q).offset((page - 1) * page_size).limit(page_size).order_by("-created_at")
|
||||
total = await AuditLog.filter(q).count()
|
||||
data = [await audit_log.to_dict() for audit_log in audit_log_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .automation import router
|
||||
|
||||
automation_router = APIRouter()
|
||||
automation_router.include_router(router, tags=["自动化模块"])
|
||||
|
||||
__all__ = ["automation_router"]
|
||||
@@ -0,0 +1,188 @@
|
||||
# app/api/v1/automation.py
|
||||
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
from app.http_base import unified_resp
|
||||
from fastapi import APIRouter, Query, Depends
|
||||
from tortoise.expressions import Q
|
||||
from app.controllers.automation.scenario import automation_scenario_controller
|
||||
from app.controllers.automation.task import task_controller
|
||||
from app.controllers.automation.action import action_controller
|
||||
from app.schemas.automation import (
|
||||
ScenarioCreate,
|
||||
ScenarioUpdate,
|
||||
ScenarioCopy,
|
||||
TaskUpdate,
|
||||
ActionUpdate,
|
||||
)
|
||||
from app.schemas.base import Success, SuccessExtra, Fail
|
||||
from app.schemas.apis import Paginate
|
||||
from ..weixin.base import get_weixin_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ============================
|
||||
# Scenario 路由
|
||||
# ============================
|
||||
|
||||
@router.get("/scenario/list", summary="查看场景列表")
|
||||
@unified_resp
|
||||
async def list_automation_scenario(
|
||||
pagination: Paginate = Depends(),
|
||||
title: str = Query("", description="场景标题,用于搜索"),
|
||||
is_global: bool = Query(None, description="是否全局场景"),
|
||||
enabled: bool = Query(None, description="是否启用"),
|
||||
owner_user_id: str = Query("", description="归属用户ID(非全局时)"),
|
||||
):
|
||||
q = Q(visible=True)
|
||||
if title:
|
||||
q &= Q(title__contains=title)
|
||||
if is_global is not None:
|
||||
q &= Q(is_global=is_global)
|
||||
if enabled is not None:
|
||||
q &= Q(enabled=enabled)
|
||||
if owner_user_id:
|
||||
q &= Q(owner_user_id=owner_user_id)
|
||||
|
||||
total, scenario_objs = await automation_scenario_controller.list(
|
||||
page=pagination.page, page_size=pagination.page_size, search=q, order=["-created_at"]
|
||||
)
|
||||
data = [await obj.to_dict() for obj in scenario_objs]
|
||||
return {"count": total, "lists": data, "page": pagination.page, "page_size": pagination.page_size}
|
||||
|
||||
|
||||
@router.get("/scenario/event_and_action", summary="查看场景事件和动作")
|
||||
@unified_resp
|
||||
async def list_event_and_action():
|
||||
actions = await action_controller.list_automation_actions()
|
||||
events = await automation_scenario_controller.list_automation_events()
|
||||
return {"events": events, "actions": actions}
|
||||
|
||||
|
||||
@router.get("/scenario/get/{id}", summary="查看场景详情")
|
||||
@unified_resp
|
||||
async def get_automation_scenario(id: int):
|
||||
obj = await automation_scenario_controller.get(id=id)
|
||||
return await obj.to_dict()
|
||||
|
||||
|
||||
@router.post("/scenario/create", summary="创建场景")
|
||||
@unified_resp
|
||||
async def create_automation_scenario(
|
||||
scenario_in: ScenarioCreate,
|
||||
weixin_user: dict = Depends(get_weixin_user)
|
||||
):
|
||||
obj = await automation_scenario_controller.new_scenario(scenario_in, weixin_user.userid)
|
||||
return await obj.to_dict()
|
||||
|
||||
|
||||
@router.post("/scenario/copy", summary="复制场景")
|
||||
@unified_resp
|
||||
async def copy_automation_scenario(
|
||||
scenario_in: ScenarioCopy,
|
||||
):
|
||||
def handler(obj_dict: Dict[str, Any]) -> Dict[str, Any]:
|
||||
obj_dict["title"] = f"复制:{obj_dict['title']}"
|
||||
obj_dict["enabled"] = False
|
||||
return obj_dict
|
||||
|
||||
obj = await automation_scenario_controller.copy(id=scenario_in.id, handler=handler)
|
||||
return await obj.to_dict()
|
||||
|
||||
|
||||
@router.post("/scenario/update", summary="更新场景")
|
||||
@unified_resp
|
||||
async def update_automation_scenario(
|
||||
scenario_in: ScenarioUpdate,
|
||||
weixin_user: dict = Depends(get_weixin_user)
|
||||
):
|
||||
obj = await automation_scenario_controller.update_scenario(scenario_in=scenario_in, user_id=weixin_user.userid)
|
||||
return await obj.to_dict()
|
||||
|
||||
|
||||
@router.delete("/scenario/delete", summary="删除场景")
|
||||
@unified_resp
|
||||
async def delete_automation_scenario(
|
||||
id: int = Query(..., description="场景ID"),
|
||||
):
|
||||
await automation_scenario_controller.remove_scenario(scenario_id=id)
|
||||
return Success(msg="Deleted Successfully")
|
||||
|
||||
|
||||
# ============================
|
||||
# Task 路由
|
||||
# ============================
|
||||
|
||||
@router.get("/task/list", summary="查看待办列表")
|
||||
@unified_resp
|
||||
async def list_task(
|
||||
pagination: Paginate = Depends(),
|
||||
assignee_user_id: str = Query("", description="指派人用户ID"),
|
||||
status: str = Query("", description="任务状态"),
|
||||
related_customer_id: str = Query("", description="关联客户ID"),
|
||||
):
|
||||
q = Q()
|
||||
if assignee_user_id:
|
||||
q &= Q(assignee_user_id=assignee_user_id)
|
||||
if status:
|
||||
q &= Q(status=status)
|
||||
if related_customer_id:
|
||||
q &= Q(related_customer_id=related_customer_id)
|
||||
|
||||
total, task_objs = await task_controller.list(
|
||||
page=pagination.page, page_size=pagination.page_size, search=q
|
||||
# , order=["-created_at"]
|
||||
)
|
||||
data = [await obj.to_dict() for obj in task_objs]
|
||||
return {"count": total, "lists": data, "page": pagination.page, "page_size": pagination.page_size}
|
||||
|
||||
|
||||
@router.get("/task/get", summary="查看待办详情")
|
||||
@unified_resp
|
||||
async def get_task(
|
||||
id: int = Query(..., description="待办ID"),
|
||||
):
|
||||
obj = await task_controller.get(id=id)
|
||||
return await obj.to_dict()
|
||||
|
||||
|
||||
@router.post("/task/update", summary="更新待办")
|
||||
@unified_resp
|
||||
async def update_task(
|
||||
task_in: TaskUpdate,
|
||||
):
|
||||
obj = await task_controller.update(id=task_in.id, obj_in=task_in)
|
||||
return await obj.to_dict()
|
||||
|
||||
|
||||
# ============================
|
||||
# Action 路由
|
||||
# ============================
|
||||
|
||||
@router.get("/action/list", summary="查看动作列表(按任务)")
|
||||
@unified_resp
|
||||
async def list_action(
|
||||
task_id: int = Query(..., description="所属任务ID"),
|
||||
):
|
||||
actions = await action_controller.model.filter(task_id=task_id).all()
|
||||
data = [await act.to_dict() for act in actions]
|
||||
return data
|
||||
|
||||
|
||||
# @router.get("/action/type", summary="查看动作类型列表(按任务)")
|
||||
# @unified_resp
|
||||
# async def list_action_type():
|
||||
# actions = await action_controller.list_automation_actions()
|
||||
# return actions
|
||||
|
||||
|
||||
@router.post("/action/update", summary="更新动作(如标记完成)")
|
||||
@unified_resp
|
||||
async def update_action(
|
||||
action_in: ActionUpdate,
|
||||
):
|
||||
obj = await action_controller.update(id=action_in.id, obj_in=action_in)
|
||||
return await obj.to_dict()
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .base import router
|
||||
|
||||
base_router = APIRouter()
|
||||
base_router.include_router(router, tags=["基础模块"])
|
||||
|
||||
__all__ = ["base_router"]
|
||||
@@ -0,0 +1,103 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.controllers.user import user_controller
|
||||
from app.core.ctx import CTX_USER_ID
|
||||
from app.core.dependency import DependAuth
|
||||
from app.models.admin import Api, Menu, Role, User
|
||||
from app.schemas.base import Fail, Success
|
||||
from app.schemas.login import *
|
||||
from app.schemas.users import UpdatePassword
|
||||
from app.settings import settings
|
||||
from app.utils.jwt import create_access_token
|
||||
from app.utils.password import get_password_hash, verify_password
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/access_token", summary="获取token")
|
||||
async def login_access_token(credentials: CredentialsSchema):
|
||||
user: User = await user_controller.authenticate(credentials)
|
||||
await user_controller.update_last_login(user.id)
|
||||
access_token_expires = timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
expire = datetime.now(timezone.utc) + access_token_expires
|
||||
|
||||
data = JWTOut(
|
||||
access_token=create_access_token(
|
||||
data=JWTPayload(
|
||||
user_id=user.id,
|
||||
username=user.username,
|
||||
is_superuser=user.is_superuser,
|
||||
exp=expire,
|
||||
)
|
||||
),
|
||||
username=user.username,
|
||||
)
|
||||
return Success(data=data.model_dump())
|
||||
|
||||
|
||||
@router.get("/userinfo", summary="查看用户信息", dependencies=[DependAuth])
|
||||
async def get_userinfo():
|
||||
user_id = CTX_USER_ID.get()
|
||||
user_obj = await user_controller.get(id=user_id)
|
||||
data = await user_obj.to_dict(exclude_fields=["password"])
|
||||
data["avatar"] = "https://avatars.githubusercontent.com/u/54677442?v=4"
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.get("/usermenu", summary="查看用户菜单", dependencies=[DependAuth])
|
||||
async def get_user_menu():
|
||||
user_id = CTX_USER_ID.get()
|
||||
user_obj = await User.filter(id=user_id).first()
|
||||
menus: list[Menu] = []
|
||||
if user_obj.is_superuser:
|
||||
menus = await Menu.all()
|
||||
else:
|
||||
role_objs: list[Role] = await user_obj.roles
|
||||
for role_obj in role_objs:
|
||||
menu = await role_obj.menus
|
||||
menus.extend(menu)
|
||||
menus = list(set(menus))
|
||||
parent_menus: list[Menu] = []
|
||||
for menu in menus:
|
||||
if menu.parent_id == 0:
|
||||
parent_menus.append(menu)
|
||||
res = []
|
||||
for parent_menu in parent_menus:
|
||||
parent_menu_dict = await parent_menu.to_dict()
|
||||
parent_menu_dict["children"] = []
|
||||
for menu in menus:
|
||||
if menu.parent_id == parent_menu.id:
|
||||
parent_menu_dict["children"].append(await menu.to_dict())
|
||||
res.append(parent_menu_dict)
|
||||
return Success(data=res)
|
||||
|
||||
|
||||
@router.get("/userapi", summary="查看用户API", dependencies=[DependAuth])
|
||||
async def get_user_api():
|
||||
user_id = CTX_USER_ID.get()
|
||||
user_obj = await User.filter(id=user_id).first()
|
||||
if user_obj.is_superuser:
|
||||
api_objs: list[Api] = await Api.all()
|
||||
apis = [api.method.lower() + api.path for api in api_objs]
|
||||
return Success(data=apis)
|
||||
role_objs: list[Role] = await user_obj.roles
|
||||
apis = []
|
||||
for role_obj in role_objs:
|
||||
api_objs: list[Api] = await role_obj.apis
|
||||
apis.extend([api.method.lower() + api.path for api in api_objs])
|
||||
apis = list(set(apis))
|
||||
return Success(data=apis)
|
||||
|
||||
|
||||
@router.post("/update_password", summary="修改密码", dependencies=[DependAuth])
|
||||
async def update_user_password(req_in: UpdatePassword):
|
||||
user_id = CTX_USER_ID.get()
|
||||
user = await user_controller.get(user_id)
|
||||
verified = verify_password(req_in.old_password, user.password)
|
||||
if not verified:
|
||||
return Fail(msg="旧密码验证错误!")
|
||||
user.password = get_password_hash(req_in.new_password)
|
||||
await user.save()
|
||||
return Success(msg="修改成功")
|
||||
@@ -0,0 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .table import router
|
||||
from .datasource import router as datasource_router
|
||||
|
||||
codegen_router = APIRouter()
|
||||
codegen_router.include_router(router, tags=["代码生成模块"])
|
||||
codegen_router.include_router(datasource_router, tags=["数据源管理"])
|
||||
|
||||
__all__ = ["codegen_router"]
|
||||
@@ -0,0 +1,73 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.exceptions import HTTPException
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.datasource import datasource_controller
|
||||
from app.schemas.base import Success, SuccessExtra
|
||||
from app.schemas.roles import *
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/datasource")
|
||||
|
||||
|
||||
@router.get("/list", summary="查看模板列表")
|
||||
async def list_role(
|
||||
page: int = Query(1, description="页码"),
|
||||
limit: int = Query(10, description="每页数量"),
|
||||
table_name: str = Query("", description="名称,用于查询"),
|
||||
):
|
||||
# data = await datasource_controller.load_tables()
|
||||
# return SuccessExtra(data=data)
|
||||
q = Q()
|
||||
if table_name:
|
||||
q = Q(name__contains=table_name)
|
||||
total, role_objs = await datasource_controller.list(page=page, page_size=limit, search=q)
|
||||
data = [await obj.to_dict() for obj in role_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=limit)
|
||||
|
||||
|
||||
@router.get("/get/{table_name}", summary="查看")
|
||||
async def get_role(table_name):
|
||||
data = await datasource_controller.load_tables(name=table_name)
|
||||
return SuccessExtra(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建")
|
||||
async def create_role(role_in: RoleCreate):
|
||||
if await datasource_controller.is_exist(name=role_in.name):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="The role with this rolename already exists in the system.",
|
||||
)
|
||||
await datasource_controller.create(obj_in=role_in)
|
||||
return Success(msg="Created Successfully")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新")
|
||||
async def update_role(role_in: RoleUpdate):
|
||||
await datasource_controller.update(id=role_in.id, obj_in=role_in)
|
||||
return Success(msg="Updated Successfully")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除")
|
||||
async def delete_role(
|
||||
role_id: int = Query(..., description="ID"),
|
||||
):
|
||||
await datasource_controller.remove(id=role_id)
|
||||
return Success(msg="Deleted Success")
|
||||
|
||||
|
||||
@router.get("/authorized", summary="查看权限")
|
||||
async def get_role_authorized(id: int = Query(..., description="ID")):
|
||||
role_obj = await datasource_controller.get(id=id)
|
||||
data = await role_obj.to_dict(m2m=True)
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/authorized", summary="更新权限")
|
||||
async def update_role_authorized(role_in: RoleUpdateMenusApis):
|
||||
role_obj = await datasource_controller.get(id=role_in.id)
|
||||
await datasource_controller.update_roles(role=role_obj, menu_ids=role_in.menu_ids, api_infos=role_in.api_infos)
|
||||
return Success(msg="Updated Successfully")
|
||||
@@ -0,0 +1,81 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.exceptions import HTTPException
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.table import codegen_controller
|
||||
from app.schemas.base import Success, SuccessExtra
|
||||
from app.schemas.codegen import *
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/table")
|
||||
|
||||
|
||||
@router.get("/page", summary="查看列表")
|
||||
async def list_role(
|
||||
page: int = Query(1, description="页码"),
|
||||
limit: int = Query(10, description="每页数量"),
|
||||
table_name: str = Query("", description="名称,用于查询"),
|
||||
):
|
||||
q = Q()
|
||||
if table_name:
|
||||
q = Q(name__contains=table_name)
|
||||
total, role_objs = await codegen_controller.list(page=page, page_size=limit, search=q)
|
||||
data = [await obj.to_dict() for obj in role_objs]
|
||||
return SuccessExtra(data=dict(list=data, total=total, page=page, page_size=limit))
|
||||
|
||||
|
||||
@router.post("/import/{db_connection}", summary="导入表")
|
||||
async def get_role(db_connection, table: dict = None):
|
||||
print('db_connection, importTables', db_connection, table)
|
||||
data = await codegen_controller.import_table(db_connection, importTables=table['table'])
|
||||
# role_obj = await codegen_controller.get(id=role_id)
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
import json
|
||||
@router.put("/field/{id}", summary="更新字段")
|
||||
async def update_fields(id: int, fields_in: dict):
|
||||
fields_in['fields'] = json.dumps(fields_in['fields'])
|
||||
await codegen_controller.update(id=id, obj_in=fields_in)
|
||||
return Success(msg="Updated Successfully")
|
||||
|
||||
@router.get("/preview/{id}", summary="代码预览")
|
||||
async def code_preview(id: int):
|
||||
data = await codegen_controller.preview(id)
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.get("/{id}", summary="查看详细的表信息")
|
||||
async def get_table_detail(id):
|
||||
table_obj = await codegen_controller.get(id=id)
|
||||
return Success(data=await table_obj.to_dict())
|
||||
|
||||
@router.post("/update", summary="更新")
|
||||
async def update_role(role_in):
|
||||
await codegen_controller.update(id=role_in.id, obj_in=role_in)
|
||||
return Success(msg="Updated Successfully")
|
||||
|
||||
|
||||
@router.delete("/", summary="删除")
|
||||
async def delete_role(
|
||||
batch_ids: dict
|
||||
):
|
||||
for id in batch_ids.get('data'):
|
||||
await codegen_controller.remove(id=id)
|
||||
return Success(msg="Deleted Success")
|
||||
|
||||
|
||||
@router.get("/authorized", summary="查看权限")
|
||||
async def get_role_authorized(id: int = Query(..., description="ID")):
|
||||
role_obj = await codegen_controller.get(id=id)
|
||||
data = await role_obj.to_dict(m2m=True)
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/authorized", summary="更新权限")
|
||||
async def update_role_authorized(role_in):
|
||||
role_obj = await codegen_controller.get(id=role_in.id)
|
||||
await codegen_controller.update_roles(role=role_obj, menu_ids=role_in.menu_ids, api_infos=role_in.api_infos)
|
||||
return Success(msg="Updated Successfully")
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .depts import router
|
||||
|
||||
depts_router = APIRouter()
|
||||
depts_router.include_router(router, tags=["部门模块"])
|
||||
|
||||
__all__ = ["depts_router"]
|
||||
@@ -0,0 +1,48 @@
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.controllers.dept import dept_controller
|
||||
from app.schemas import Success
|
||||
from app.schemas.depts import *
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看部门列表")
|
||||
async def list_dept(
|
||||
name: str = Query(None, description="部门名称"),
|
||||
):
|
||||
dept_tree = await dept_controller.get_dept_tree(name)
|
||||
return Success(data=dept_tree)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看部门")
|
||||
async def get_dept(
|
||||
id: int = Query(..., description="部门ID"),
|
||||
):
|
||||
dept_obj = await dept_controller.get(id=id)
|
||||
data = await dept_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建部门")
|
||||
async def create_dept(
|
||||
dept_in: DeptCreate,
|
||||
):
|
||||
await dept_controller.create_dept(obj_in=dept_in)
|
||||
return Success(msg="Created Successfully")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新部门")
|
||||
async def update_dept(
|
||||
dept_in: DeptUpdate,
|
||||
):
|
||||
await dept_controller.update_dept(obj_in=dept_in)
|
||||
return Success(msg="Update Successfully")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除部门")
|
||||
async def delete_dept(
|
||||
dept_id: int = Query(..., description="部门ID"),
|
||||
):
|
||||
await dept_controller.delete_dept(dept_id=dept_id)
|
||||
return Success(msg="Deleted Success")
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .finance_parse import router
|
||||
|
||||
finance_parse_router = APIRouter()
|
||||
finance_parse_router.include_router(router, tags=["订单备注解析模块"])
|
||||
|
||||
__all__ = ["finance_parse_router"]
|
||||
@@ -0,0 +1,95 @@
|
||||
from fastapi import APIRouter, File, UploadFile, Query
|
||||
from fastapi.responses import FileResponse
|
||||
from app.schemas import Success, SuccessExtra, Fail
|
||||
from app.controllers.finance_parse import task_controller, parse_finance_data
|
||||
import os, time
|
||||
from app.schemas.task import DecodeTaskParams, DecodeTaskResult, TaskResponse
|
||||
from app.models.automation import TaskStatus, TaskType
|
||||
from datetime import datetime
|
||||
router = APIRouter()
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from app.utils.excel_utils import get_sheets_and_headers
|
||||
|
||||
|
||||
@router.post("/upload", summary="上传文件")
|
||||
async def upload_file(file: UploadFile = File(...)):
|
||||
# file.filename: 原始文件名
|
||||
# file.content_type: MIME 类型
|
||||
# file.file: 类文件对象(SpooledTemporaryFile)
|
||||
|
||||
# 保存文件到本地(示例:保存到 ./uploads/)
|
||||
upload_dir = Path("uploads")
|
||||
upload_dir.mkdir(exist_ok=True)
|
||||
filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}"
|
||||
|
||||
file_path = upload_dir / filename
|
||||
with open(file_path, "wb") as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
|
||||
# 获取所有 sheet 名称及其表头
|
||||
sheets_headers = get_sheets_and_headers(file_path)
|
||||
|
||||
# # 打印结果
|
||||
# for sheet_name, headers in sheets_headers.items():
|
||||
# print(f"Sheet: {sheet_name}")
|
||||
# print(f"Headers: {headers}\n")
|
||||
|
||||
data = {
|
||||
"filename": filename,
|
||||
"data": sheets_headers,
|
||||
}
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/parse", summary="解析文件")
|
||||
async def parse_file(data: dict):
|
||||
upload_dir = Path("uploads")
|
||||
filename = upload_dir / data["filename"]
|
||||
sheet = data["sheet"]
|
||||
header = data["header"]
|
||||
parse_type = data["parse_type"]
|
||||
taskname = data["filename"] + "_" + sheet
|
||||
|
||||
print(filename, sheet, header, parse_type)
|
||||
task, task_obj = await task_controller.create_task(name=taskname, obj_in=DecodeTaskParams(filename=str(filename), sheet_name=sheet, header=header, decode_type=parse_type))
|
||||
total_amount = 0
|
||||
start = time.time()
|
||||
|
||||
try:
|
||||
result_file_path, total_amount = parse_finance_data(
|
||||
filename,
|
||||
target_index=header,
|
||||
is_horizontal=(parse_type == "horizontal"),
|
||||
sheet_name=sheet
|
||||
)
|
||||
except Exception as e:
|
||||
return Fail(msg=f"解析失败: {str(e)}", code=400)
|
||||
|
||||
if not os.path.exists(result_file_path):
|
||||
return Fail(msg=f"解析结果文件未生成", code=404)
|
||||
|
||||
# 提取原始文件名(不含路径),用于下载时的默认文件名
|
||||
download_filename = os.path.basename(result_file_path)
|
||||
await task_controller.update_task(task_obj, task.id, TaskStatus.SUCCESS, DecodeTaskResult(
|
||||
filename=str(result_file_path),
|
||||
spend=time.time() - start,
|
||||
rows=total_amount,
|
||||
))
|
||||
|
||||
return FileResponse(
|
||||
path=result_file_path,
|
||||
filename=download_filename, # 浏览器下载时显示的文件名
|
||||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' # .xlsx
|
||||
)
|
||||
|
||||
@router.get("/list", summary="获取任务列表")
|
||||
async def get_tasks(page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
name: str = Query("", description="任务名称,用于查询"),
|
||||
type: TaskType = Query(None, description="任务类型,用于查询")):
|
||||
total, tasks = await task_controller.list(name, type, page, page_size)
|
||||
data = [TaskResponse.from_orm(task).model_dump() for task in tasks]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .menus import router
|
||||
|
||||
menus_router = APIRouter()
|
||||
menus_router.include_router(router, tags=["菜单模块"])
|
||||
|
||||
__all__ = ["menus_router"]
|
||||
@@ -0,0 +1,63 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.controllers.menu import menu_controller
|
||||
from app.schemas.base import Fail, Success, SuccessExtra
|
||||
from app.schemas.menus import *
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看菜单列表")
|
||||
async def list_menu(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
):
|
||||
async def get_menu_with_children(menu_id: int):
|
||||
menu = await menu_controller.model.get(id=menu_id)
|
||||
menu_dict = await menu.to_dict()
|
||||
child_menus = await menu_controller.model.filter(parent_id=menu_id).order_by("order")
|
||||
menu_dict["children"] = [await get_menu_with_children(child.id) for child in child_menus]
|
||||
return menu_dict
|
||||
|
||||
parent_menus = await menu_controller.model.filter(parent_id=0).order_by("order")
|
||||
res_menu = [await get_menu_with_children(menu.id) for menu in parent_menus]
|
||||
return SuccessExtra(data=res_menu, total=len(res_menu), page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看菜单")
|
||||
async def get_menu(
|
||||
menu_id: int = Query(..., description="菜单id"),
|
||||
):
|
||||
result = await menu_controller.get(id=menu_id)
|
||||
return Success(data=result)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建菜单")
|
||||
async def create_menu(
|
||||
menu_in: MenuCreate,
|
||||
):
|
||||
await menu_controller.create(obj_in=menu_in)
|
||||
return Success(msg="Created Success")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新菜单")
|
||||
async def update_menu(
|
||||
menu_in: MenuUpdate,
|
||||
):
|
||||
await menu_controller.update(id=menu_in.id, obj_in=menu_in)
|
||||
return Success(msg="Updated Success")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除菜单")
|
||||
async def delete_menu(
|
||||
id: int = Query(..., description="菜单id"),
|
||||
):
|
||||
child_menu_count = await menu_controller.model.filter(parent_id=id).count()
|
||||
if child_menu_count > 0:
|
||||
return Fail(msg="Cannot delete a menu with child menus")
|
||||
await menu_controller.remove(id=id)
|
||||
return Success(msg="Deleted Success")
|
||||
@@ -0,0 +1,9 @@
|
||||
from fastapi import APIRouter
|
||||
from .msg import router as msg_router
|
||||
from .event import router as event_router
|
||||
|
||||
msg_route_router = APIRouter()
|
||||
msg_route_router.include_router(msg_router, tags=["消息推送模块"])
|
||||
msg_route_router.include_router(event_router, tags=["事件模块"])
|
||||
|
||||
__all__ = ["msg_route_router"]
|
||||
@@ -0,0 +1,15 @@
|
||||
# app/api/wechat.py
|
||||
from fastapi import APIRouter
|
||||
from app.schemas.msg import FeishuEvent
|
||||
from app.http_base import unified_resp
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/event")
|
||||
|
||||
# 群聊入口
|
||||
@router.post("/feishu", summary="通过群ID获取到群信息以及同步数据到本地")
|
||||
async def feishu(feishuEvent: FeishuEvent):
|
||||
return {"challenge": feishuEvent.challenge}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# app/api/wechat.py
|
||||
from fastapi import APIRouter, Depends
|
||||
from app.controllers.msg import msg_controller
|
||||
from app.controllers.weixin.customer import weixin_customer_controller
|
||||
from app.schemas.msg import MsgNewOrder, MsgFilter, MsgUpdate
|
||||
from app.http_base import unified_resp
|
||||
from app.schemas.apis import Paginate
|
||||
from tortoise.expressions import Q
|
||||
from ..weixin.base import get_weixin_user
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix='/msg')
|
||||
|
||||
# 群聊入口
|
||||
@router.post("/new_order", summary="通过群ID获取到群信息以及同步数据到本地")
|
||||
@unified_resp
|
||||
async def create_new_order(msg: MsgNewOrder):
|
||||
|
||||
if msg.buyer_id.startswith('*******'):
|
||||
return {"msg": "不支持的客户类型"}
|
||||
|
||||
if msg.buyer_nick:
|
||||
weixin_customer = await weixin_customer_controller.model.filter(taobao_name=msg.buyer_nick).first()
|
||||
elif msg.buyer_id:
|
||||
weixin_customer = await weixin_customer_controller.model.filter(weixin_id=msg.buyer_id).first()
|
||||
else:
|
||||
return {"msg": "buyer_id or buyer_nick is required"}
|
||||
|
||||
if not weixin_customer:
|
||||
return {"msg": "当前客户未绑定微信账号"}
|
||||
|
||||
logger.info(f'用户新订单: {msg}')
|
||||
|
||||
order_id = msg.order_id
|
||||
|
||||
await msg_controller.new_order(order_id, weixin_customer, is_refund=msg.is_refund)
|
||||
await msg_controller.sync_msg()
|
||||
return {"msg": "success"}
|
||||
|
||||
|
||||
@router.get("/fill_customer_info", summary="填充客户信息")
|
||||
@unified_resp
|
||||
async def fill_customer_info():
|
||||
await msg_controller.fill_customer_info()
|
||||
return {"msg": "success"}
|
||||
|
||||
@router.get("/list", summary="通过群ID获取到群信息以及同步数据到本地")
|
||||
@unified_resp
|
||||
async def list_msg(paginate: Paginate = Depends(), order: str = "-id", msg_filter: MsgFilter = Depends(), weixin_user: dict = Depends(get_weixin_user)):
|
||||
print('paginate', paginate.page, paginate.page_size)
|
||||
print('msg_filter', msg_filter)
|
||||
print('weixin_user', weixin_user.username, weixin_user.userid)
|
||||
# q = Q(owner_id='11')
|
||||
q = Q(owner_id=weixin_user.userid)
|
||||
if msg_filter.type is not None:
|
||||
q &= Q(type=msg_filter.type)
|
||||
if msg_filter.is_read is not None:
|
||||
q &= Q(is_read=msg_filter.is_read)
|
||||
if msg_filter.is_read:
|
||||
order = '-read_at'
|
||||
|
||||
if msg_filter.is_refund is not None:
|
||||
q &= Q(detail__contains={"is_refund": 1 if msg_filter.is_refund else 0})
|
||||
|
||||
total, msg_list = await msg_controller.list(paginate.page, paginate.page_size, order=[order], search=q)
|
||||
return {"count": total, "lists": [ await msg.to_dict() for msg in msg_list]}
|
||||
|
||||
@router.post("/set_read", summary="设置消息为已读")
|
||||
@unified_resp
|
||||
async def set_read(msg: MsgUpdate, weixin_user: dict = Depends(get_weixin_user)):
|
||||
await msg_controller.set_read(msg.id)
|
||||
return {"msg": "success"}
|
||||
|
||||
@router.get("/get_order_user_days_before", summary="获取指定天数前的订单用户")
|
||||
@unified_resp
|
||||
async def get_order_user_days_before():
|
||||
return await msg_controller.get_order_user_days_before(2)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .roles import router
|
||||
|
||||
roles_router = APIRouter()
|
||||
roles_router.include_router(router, tags=["角色模块"])
|
||||
|
||||
__all__ = ["roles_router"]
|
||||
@@ -0,0 +1,73 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.exceptions import HTTPException
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers import role_controller
|
||||
from app.schemas.base import Success, SuccessExtra
|
||||
from app.schemas.roles import *
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看角色列表")
|
||||
async def list_role(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
role_name: str = Query("", description="角色名称,用于查询"),
|
||||
):
|
||||
q = Q()
|
||||
if role_name:
|
||||
q = Q(name__contains=role_name)
|
||||
total, role_objs = await role_controller.list(page=page, page_size=page_size, search=q)
|
||||
data = [await obj.to_dict() for obj in role_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看角色")
|
||||
async def get_role(
|
||||
role_id: int = Query(..., description="角色ID"),
|
||||
):
|
||||
role_obj = await role_controller.get(id=role_id)
|
||||
return Success(data=await role_obj.to_dict())
|
||||
|
||||
|
||||
@router.post("/create", summary="创建角色")
|
||||
async def create_role(role_in: RoleCreate):
|
||||
if await role_controller.is_exist(name=role_in.name):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="The role with this rolename already exists in the system.",
|
||||
)
|
||||
await role_controller.create(obj_in=role_in)
|
||||
return Success(msg="Created Successfully")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新角色")
|
||||
async def update_role(role_in: RoleUpdate):
|
||||
await role_controller.update(id=role_in.id, obj_in=role_in)
|
||||
return Success(msg="Updated Successfully")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除角色")
|
||||
async def delete_role(
|
||||
role_id: int = Query(..., description="角色ID"),
|
||||
):
|
||||
await role_controller.remove(id=role_id)
|
||||
return Success(msg="Deleted Success")
|
||||
|
||||
|
||||
@router.get("/authorized", summary="查看角色权限")
|
||||
async def get_role_authorized(id: int = Query(..., description="角色ID")):
|
||||
role_obj = await role_controller.get(id=id)
|
||||
data = await role_obj.to_dict(m2m=True)
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/authorized", summary="更新角色权限")
|
||||
async def update_role_authorized(role_in: RoleUpdateMenusApis):
|
||||
role_obj = await role_controller.get(id=role_in.id)
|
||||
await role_controller.update_roles(role=role_obj, menu_ids=role_in.menu_ids, api_infos=role_in.api_infos)
|
||||
return Success(msg="Updated Successfully")
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .users import router
|
||||
|
||||
users_router = APIRouter()
|
||||
users_router.include_router(router, tags=["用户模块"])
|
||||
|
||||
__all__ = ["users_router"]
|
||||
@@ -0,0 +1,87 @@
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Body, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.dept import dept_controller
|
||||
from app.controllers.user import user_controller
|
||||
from app.schemas.base import Fail, Success, SuccessExtra
|
||||
from app.schemas.users import *
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看用户列表")
|
||||
async def list_user(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
username: str = Query("", description="用户名称,用于搜索"),
|
||||
email: str = Query("", description="邮箱地址"),
|
||||
dept_id: int = Query(None, description="部门ID"),
|
||||
):
|
||||
q = Q()
|
||||
if username:
|
||||
q &= Q(username__contains=username)
|
||||
if email:
|
||||
q &= Q(email__contains=email)
|
||||
if dept_id is not None:
|
||||
q &= Q(dept_id=dept_id)
|
||||
total, user_objs = await user_controller.list(page=page, page_size=page_size, search=q)
|
||||
data = [await obj.to_dict(m2m=True, exclude_fields=["password"]) for obj in user_objs]
|
||||
for item in data:
|
||||
dept_id = item.pop("dept_id", None)
|
||||
item["dept"] = await (await dept_controller.get(id=dept_id)).to_dict() if dept_id else {}
|
||||
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看用户")
|
||||
async def get_user(
|
||||
user_id: int = Query(..., description="用户ID"),
|
||||
):
|
||||
user_obj = await user_controller.get(id=user_id)
|
||||
user_dict = await user_obj.to_dict(exclude_fields=["password"])
|
||||
return Success(data=user_dict)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建用户")
|
||||
async def create_user(
|
||||
user_in: UserCreate,
|
||||
):
|
||||
user = await user_controller.get_by_email(user_in.email)
|
||||
if user:
|
||||
return Fail(code=400, msg="The user with this email already exists in the system.")
|
||||
new_user = await user_controller.create_user(obj_in=user_in)
|
||||
await user_controller.update_roles(new_user, user_in.role_ids)
|
||||
return Success(msg="Created Successfully")
|
||||
|
||||
|
||||
@router.post("/update", summary="用户管理中更新用户")
|
||||
async def update_user(
|
||||
user_in: UserUpdate,
|
||||
):
|
||||
user = await user_controller.update(id=user_in.id, obj_in=user_in)
|
||||
await user_controller.update_roles(user, user_in.role_ids)
|
||||
return Success(msg="Updated Successfully")
|
||||
|
||||
@router.post("/update_user_online", summary="更新用户")
|
||||
async def update_user_online(
|
||||
user_in: UserUpdateOnline,
|
||||
):
|
||||
user = await user_controller.update(id=user_in.id, obj_in=user_in)
|
||||
return Success(msg="Updated Successfully")
|
||||
|
||||
@router.delete("/delete", summary="删除用户")
|
||||
async def delete_user(
|
||||
user_id: int = Query(..., description="用户ID"),
|
||||
):
|
||||
await user_controller.remove(id=user_id)
|
||||
return Success(msg="Deleted Successfully")
|
||||
|
||||
|
||||
@router.post("/reset_password", summary="重置密码")
|
||||
async def reset_password(user_id: int = Body(..., description="用户ID", embed=True)):
|
||||
await user_controller.reset_password(user_id)
|
||||
return Success(msg="密码已重置为123456")
|
||||
@@ -0,0 +1,11 @@
|
||||
from fastapi import APIRouter
|
||||
from app.controllers.weixin import schedule as _schedule
|
||||
from .weixin import router
|
||||
from .weixin_user import router as weixin_user_router
|
||||
|
||||
|
||||
weixin_router = APIRouter()
|
||||
weixin_router.include_router(router, tags=["微信模块"])
|
||||
weixin_router.include_router(weixin_user_router, tags=["微信用户模块"])
|
||||
|
||||
__all__ = ["weixin_router"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# app/core/dependency.py
|
||||
from typing import Annotated
|
||||
from fastapi import Header, HTTPException, Depends
|
||||
from app.core.ctx import set_ctx_weixin_user
|
||||
from app.models import WeixinUser
|
||||
|
||||
async def get_weixin_user(token: str = Header(..., description="token验证")) -> WeixinUser:
|
||||
user = await WeixinUser.filter(userid=token).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=401, detail="Weixin User Authentication failed")
|
||||
# CTX_USER_ID.set(int(user.id))
|
||||
set_ctx_weixin_user(int(user.id), f'{user.username}({user.english_name})')
|
||||
return user
|
||||
|
||||
DependWeixinUser = Depends(get_weixin_user)
|
||||
WeixinUserDep = Annotated[WeixinUser, DependWeixinUser]
|
||||
@@ -0,0 +1,2 @@
|
||||
httpx
|
||||
pypinyin
|
||||
@@ -0,0 +1,95 @@
|
||||
# app/api/wechat.py
|
||||
from fastapi import APIRouter, Query
|
||||
# from app.controllers import wechat_sdk
|
||||
# from app.controllers.weixin_script import WeixinUser_controller
|
||||
from some_sdk.services.binder import xy_client
|
||||
from some_sdk.wk_weixin_sdk import auth
|
||||
from some_sdk.wk_weixin_sdk.apis import corp_group, extern_user
|
||||
from app.http_base import unified_resp
|
||||
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# @router.get("/app-config")
|
||||
# @unified_resp
|
||||
# def get_config():
|
||||
# return wechat_sdk.get_base_config()
|
||||
|
||||
# 获取企业身份签名
|
||||
@router.get("/jssdk-config", summary="获取企业微信 企业身份 JS-SDK 配置")
|
||||
@unified_resp
|
||||
async def jssdk_config(type: str = Query(..., description="类型"), url: str = Query(..., description="前端当前页面 URL(不含 #)")):
|
||||
return await auth.get_jssdk_config(type, url)
|
||||
|
||||
@router.get("/get_access_user_info", summary="获取访问用户身份")
|
||||
@unified_resp
|
||||
async def get_access_user_info(code: str = Query(..., description="code为企业成员点击了构造链接之后附加在url中参数")):
|
||||
return await auth.get_access_user_info(code)
|
||||
|
||||
@router.get("/get_external_group_chat_info", summary="获取外部群聊详细信息")
|
||||
@unified_resp
|
||||
async def get_external_group_chat_info(chat_id: str = Query(..., description="群对话ID")):
|
||||
return await corp_group.get_external_group_chat_info(xy_client, chat_id)
|
||||
|
||||
@router.get("/get_external_user_chat_info", summary="获取外部客户详细信息")
|
||||
@unified_resp
|
||||
async def get_external_user_chat_info(user_id: str = Query(..., description="为外部客户的userid")):
|
||||
return await extern_user.get_external_user_chat_info(user_id)
|
||||
|
||||
# @router.get("/get_external_user_list", summary="获取指定用户的所有外部客户")
|
||||
# @unified_resp
|
||||
# async def get_external_user_list(user_id: str = Query(..., description="企业成员的userid")):
|
||||
# return await wechat_sdk.get_external_user_list(user_id)
|
||||
|
||||
# @router.get("/get_corp_user_id_list", summary="获取指公司所有员工")
|
||||
# @unified_resp
|
||||
# async def get_corp_user_id_list():
|
||||
# return await wechat_sdk.get_corp_user_id_list()
|
||||
|
||||
# @router.get("/get_follow_user_list", summary="获取配置了客户联系功能的成员列表")
|
||||
# @unified_resp
|
||||
# async def get_follow_user_list():
|
||||
# return await wechat_sdk.get_follow_user_list()
|
||||
|
||||
# @router.get("/get_follow_user_list", summary="获取配置了客户联系功能的成员列表")
|
||||
# @unified_resp
|
||||
# async def get_follow_user_list():
|
||||
# return await wechat_sdk.get_follow_user_list()
|
||||
|
||||
# @router.get("/get_groupchat_list", summary="获取指定用户的客户群列表")
|
||||
# @unified_resp
|
||||
# async def get_groupchat_list():
|
||||
# return await wechat_sdk.get_groupchat_list(['XiYinShuo'])
|
||||
|
||||
# @router.get("/update_all_user", summary="同步所有用户信息")
|
||||
# @unified_resp
|
||||
# async def update_all_user():
|
||||
# return await WeixinUser_controller.update_all_user()
|
||||
|
||||
# @router.get("/update_all_user_by_group_chat", summary="通过群聊同步用户信息")
|
||||
# @unified_resp
|
||||
# async def update_all_user_by_group_chat():
|
||||
# return await WeixinUser_controller.update_all_user_by_group_chat()
|
||||
|
||||
# @router.get("/convert_extenal_userid", summary="通过群聊同步用户信息")
|
||||
# @unified_resp
|
||||
# async def convert_extenal_userid():
|
||||
# return await wechat_sdk.convert_extenal_userid('wmKgOaDQAA37oxGfhBFCAFPqmICXNAZA')
|
||||
|
||||
# @router.get("/update_all_user_by_xingyun", summary="通过外部系统同步用户信息")
|
||||
# @unified_resp
|
||||
# async def update_all_user_by_xingyun():
|
||||
# return await WeixinUser_controller.update_all_user_by_xingyun()
|
||||
|
||||
# @router.get("/convert_extenal_userid", summary="转换三方应用的extenal_userid")
|
||||
# @unified_resp
|
||||
# async def convert_extenal_userid(user_id: str = Query(..., description="客户的userid")):
|
||||
# return await wechat_sdk.convert_extenal_userid(user_id)
|
||||
|
||||
# @router.get("/get_external_contact_list", summary="获取已服务的外部联系人")
|
||||
# @unified_resp
|
||||
# async def get_external_contact_list(cursor: Optional[str] = Query(None, description="客户的userid")):
|
||||
# return await wechat_sdk.get_external_contact_list(cursor)
|
||||
@@ -0,0 +1,294 @@
|
||||
# app/api/wechat.py
|
||||
from fastapi import APIRouter, Query, BackgroundTasks, Depends
|
||||
from tortoise.expressions import Q
|
||||
from app.controllers.weixin.utils import get_buyer_nick_from_group_name
|
||||
from app.controllers.weixin.group import weixin_group_chat_controller
|
||||
from app.controllers.weixin.customer import weixin_customer_controller
|
||||
from app.controllers.weixin.user import weixin_user_controller
|
||||
from app.controllers.action import action_controller
|
||||
from app.schemas.weixin import WeixinUserBindInfo, WeixinOrderBindInfo, TriggerWeixinGroupChat, WeixinOrderRemark, WeixinCustomerFilter, WeixinCustomerCreate
|
||||
from app.http_base import unified_resp
|
||||
from app.schemas.apis import Paginate
|
||||
from .base import get_weixin_user
|
||||
|
||||
from .base import WeixinUserDep
|
||||
from app.models.msg import ActionType
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from app.utils.event_task import event_manager, EventType
|
||||
from app.core.cache import cache_if
|
||||
# from app.schemas.crm import CrmBindInfoCreate, CrmBindInfoUpdate
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="")
|
||||
|
||||
# 废弃
|
||||
@router.post("/list_user_join_group", summary="加载用户加入的群聊列表")
|
||||
@unified_resp
|
||||
async def list_user_join_group(customer: WeixinCustomerCreate, weixin_user: dict = Depends(get_weixin_user)):
|
||||
print(f'list_user_join_group, customer: {customer}')
|
||||
# user_join_group = await weixin_group_chat_controller.list_user_join_group(customer.xingyun_id)
|
||||
user_join_group = await weixin_customer_controller.get_user_detail(customer.weixin_id)
|
||||
return user_join_group
|
||||
|
||||
@router.get("/list_customer", summary="加载客户列表")
|
||||
@unified_resp
|
||||
async def list_msg(paginate: Paginate = Depends(), order: str = "-id", customer_filter: WeixinCustomerFilter = Depends(), weixin_user: dict = Depends(get_weixin_user)):
|
||||
print('paginate', paginate.page, paginate.page_size)
|
||||
print('customer_filter', customer_filter)
|
||||
print('weixin_user', weixin_user.username, weixin_user.userid)
|
||||
|
||||
q = Q()
|
||||
data = {}
|
||||
if customer_filter.order_id is not None:
|
||||
q &= Q(order_id=customer_filter.order_id)
|
||||
data['order_id'] = customer_filter.order_id
|
||||
if customer_filter.shop_name is not None:
|
||||
q &= Q(shop_name__contains=customer_filter.shop_name)
|
||||
data['shop_name'] = customer_filter.shop_name
|
||||
if customer_filter.union_id is not None:
|
||||
# 是或者的关系 weixin_id\xingyun_id\taobao_id\union_id
|
||||
q &= (Q(weixin_id=customer_filter.union_id) | Q(xingyun_id=customer_filter.union_id) | Q(taobao_id=customer_filter.union_id) | Q(union_id=customer_filter.union_id))
|
||||
data['union_id'] = customer_filter.union_id
|
||||
if customer_filter.union_name is not None:
|
||||
# 是或者的关系 weixin_username\xingyun_name\taobao_name
|
||||
q &= (Q(weixin_name__contains=customer_filter.union_name) | Q(xingyun_name__contains=customer_filter.union_name) | Q(taobao_name__contains=customer_filter.union_name))
|
||||
data['union_name'] = customer_filter.union_name
|
||||
if customer_filter.xingyun_tags is not None:
|
||||
q &= Q(xingyun_tags__contains={"tagName": customer_filter.xingyun_tags})
|
||||
data['xingyun_tags'] = customer_filter.xingyun_tags
|
||||
if customer_filter.has_order is not None:
|
||||
q &= Q(order_id__isnull=not customer_filter.has_order)
|
||||
data['has_order'] = customer_filter.has_order
|
||||
|
||||
# 打印查询条件明文
|
||||
print('raw query', data)
|
||||
|
||||
total, user_list = await weixin_customer_controller.list(paginate.page, paginate.page_size, order=[order], search=q)
|
||||
|
||||
lists = []
|
||||
for user in user_list:
|
||||
user_info = user.dump_dict()
|
||||
lists.append(user_info)
|
||||
user_info['user_join_group'] = await weixin_customer_controller.get_user_detail(user.weixin_id)
|
||||
|
||||
return {"count": total, "lists": lists}
|
||||
|
||||
|
||||
# 群聊入口
|
||||
@router.get("/get_user_info", summary="通过群ID获取到群信息以及同步数据到本地")
|
||||
@unified_resp
|
||||
async def get_external_group_chat_info(userid: str):
|
||||
weixin_user = await weixin_user_controller.model.filter(userid=userid).first()
|
||||
return weixin_user.to_dict()
|
||||
|
||||
# 群聊入口
|
||||
@router.get("/group/get_external_group_chat_info", summary="通过群ID获取到群信息以及同步数据到本地")
|
||||
@unified_resp
|
||||
async def get_external_group_chat_info(chat_id: str, background_tasks: BackgroundTasks):
|
||||
group_info = await weixin_group_chat_controller.get_external_group_chat_info(chat_id)
|
||||
|
||||
if group_info['detect_update']:
|
||||
await event_manager.publish_async(EventType.GROUP_CHAT_UPDATED, group_info, background_tasks)
|
||||
|
||||
return group_info
|
||||
|
||||
|
||||
@router.get("/group/get_order_relative_user_list_by_weixin_groupid", summary="通过群ID获取订单相关用户列表")
|
||||
@unified_resp
|
||||
async def get_order_relative_user_list_by_weixin_groupid(chat_id: str = Query(..., description="群ID"), background_tasks: BackgroundTasks = None):
|
||||
async with cache_if(f'order:chat:{chat_id}', ttl=60*3) as cache:
|
||||
if cache.hit:
|
||||
order_list = cache.value
|
||||
if order_list:
|
||||
order_list[0] = await weixin_group_chat_controller.get_user_detail_by_order(order_id=order_list[0]['ctid'])
|
||||
else:
|
||||
order_list = await _get_order_relative_user_list_by_weixin_groupid(chat_id, background_tasks)
|
||||
cache.set(order_list)
|
||||
return order_list
|
||||
|
||||
async def _get_order_relative_user_list_by_weixin_groupid(chat_id: str = Query(..., description="群ID"), background_tasks: BackgroundTasks = None):
|
||||
group_info = await weixin_group_chat_controller.get_external_group_chat_info(chat_id)
|
||||
logger.info(f'group_info: {group_info}')
|
||||
|
||||
if group_info['detect_update'] or True:
|
||||
await event_manager.publish_async(EventType.GROUP_CHAT_UPDATED, group_info, background_tasks)
|
||||
|
||||
name = group_info.get('name')
|
||||
logger.debug(f'群聊名称: {name} chat_id: {chat_id}')
|
||||
|
||||
user_order_list = []
|
||||
|
||||
external_member_list = group_info['external_member_list']
|
||||
if external_member_list:
|
||||
userid_list = [member['userid'] for member in external_member_list if member.get('userid')]
|
||||
logger.debug(f'userid_list: {userid_list}')
|
||||
customer_list = await weixin_customer_controller.model.filter(weixin_id__in=userid_list, taobao_id__isnull=False).all()
|
||||
for customer in customer_list:
|
||||
buyer = customer.taobao_name or customer.taobao_id
|
||||
if not buyer: continue
|
||||
|
||||
logger.debug(f'buyer: {buyer}')
|
||||
result, order_list = await weixin_group_chat_controller.get_order_relative_user_list_by_weixin_userid(customer.weixin_id)
|
||||
if not result: continue
|
||||
user_order_list = order_list
|
||||
break
|
||||
# return await weixin_group_chat_controller.get_order_relative_user_list_by_weixin_group_name(buyer_nick)
|
||||
# return await weixin_group_chat_controller.get_order_relative_user_list_by_weixin_group_buyer_id(buyer_id)
|
||||
|
||||
if not user_order_list:
|
||||
logger.warning(f'群内成员没有订单,将根据群聊名称 {name} 来获取订单相关用户列表')
|
||||
assert name, "当前群聊未命名"
|
||||
buyer_nick = get_buyer_nick_from_group_name(name)
|
||||
logger.debug(f'name: {name}; buyer_nick: {buyer_nick}')
|
||||
assert buyer_nick, "当前群聊未命名"
|
||||
user_order_list = await weixin_group_chat_controller.get_order_relative_user_list_by_weixin_group_name(buyer_nick)
|
||||
|
||||
if group_info['detect_update']:
|
||||
try:
|
||||
await action_controller.check_create_group_action_is_done(group_info, user_order_list[0]['shop_name'] if user_order_list else None)
|
||||
except Exception as e:
|
||||
logger.exception(f'检查创建群聊动作是否完成失败: {e}')
|
||||
|
||||
return user_order_list
|
||||
|
||||
|
||||
|
||||
# @router.get("/group/load_xingyun_group_info", summary="通过群ID获取订单相关用户列表")
|
||||
# @unified_resp
|
||||
# async def load_xingyun_group_info():
|
||||
# return await weixin_group_chat_controller.load_xingyun_group_info()
|
||||
|
||||
|
||||
@router.get("/get_user_detail_by_order", summary="通过订单ID获取订单相关用户详情")
|
||||
@unified_resp
|
||||
async def get_user_detail_by_order(order_id: str):
|
||||
return await weixin_group_chat_controller.get_user_detail_by_order(order_id)
|
||||
|
||||
|
||||
@router.get("/get_erp_order_log", summary="通过订单ID获取订单日志")
|
||||
@unified_resp
|
||||
async def get_erp_order_log(order_id: str, login_user: WeixinUserDep):
|
||||
result = await weixin_group_chat_controller.get_erp_order_log(order_id)
|
||||
try:
|
||||
await action_controller.new_action(ActionType.VIEW_ERP_LOG, login_user, order_id=order_id)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f'员工操作日志记录失败')
|
||||
return result
|
||||
|
||||
|
||||
# 通过私聊入口
|
||||
@router.get("/get_order_relative_user_list_by_weixin_userid", summary="通过用户ID获取订单相关用户列表")
|
||||
@unified_resp
|
||||
async def get_order_relative_user_list_by_weixin_userid(userid: str, background_tasks: BackgroundTasks):
|
||||
result, order_list = await weixin_group_chat_controller.get_order_relative_user_list_by_weixin_userid(userid)
|
||||
if not result:
|
||||
await event_manager.publish_async(EventType.OPEN_PERSONAL_CHAT, {"userid": userid}, background_tasks)
|
||||
|
||||
return order_list
|
||||
|
||||
|
||||
@router.post("/bind_order", summary="绑定订单")
|
||||
@unified_resp
|
||||
async def bind_order(bind_in: WeixinOrderBindInfo, background_tasks: BackgroundTasks, login_user: WeixinUserDep):
|
||||
bind_in.userid = bind_in.userid.strip()
|
||||
bind_in.order_id = bind_in.order_id.strip()
|
||||
logger.info(f'【绑定订单】userid: {bind_in.userid}; order_id: {bind_in.order_id}')
|
||||
|
||||
old_info = await weixin_customer_controller.model.filter(weixin_id=bind_in.userid, order_id=bind_in.order_id).first()
|
||||
assert not old_info, "不用为客户绑定相同订单"
|
||||
|
||||
result, orders = await weixin_customer_controller.bind_order(bind_in)
|
||||
try:
|
||||
await action_controller.new_action(ActionType.BIND_ORDER, login_user, **bind_in.model_dump(exclude_unset=True))
|
||||
if orders and bind_in.remark:
|
||||
orders = [{
|
||||
"title": order.get("title"),
|
||||
"ctid": order.get("ctid"),
|
||||
"trade_no": order.get("trade_no"),
|
||||
"remark": order.get("remark", ""),
|
||||
} for order in orders]
|
||||
|
||||
need_monitor_order_list = await weixin_customer_controller.remark_order_by_order_id(remark=bind_in.remark, orders=orders)
|
||||
await action_controller.new_action(ActionType.WRITE_REMARK, login_user, order_id=bind_in.order_id, remark=bind_in.remark, orders=need_monitor_order_list, done=len(need_monitor_order_list) == 0)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f'员工操作日志记录失败')
|
||||
|
||||
# 用户触发绑定时,及时同步到星云有客中
|
||||
await event_manager.publish_async(EventType.BIND_ORDER_FOR_USER, orders[0] if orders else {}, background_tasks)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/remark_order", summary="为订单添加额外备注")
|
||||
@unified_resp
|
||||
async def remark_order(remark_in: WeixinOrderRemark, login_user: WeixinUserDep):
|
||||
if not remark_in.remark:
|
||||
logger.warning(f'订单备注为空,将不执行任何操作')
|
||||
return False
|
||||
|
||||
try:
|
||||
need_monitor_order_list = await weixin_customer_controller.remark_order_by_order_id(remark=remark_in.remark, order_id=remark_in.order_id)
|
||||
await action_controller.new_action(ActionType.WRITE_REMARK, login_user, order_id=remark_in.order_id, remark=remark_in.remark, orders=need_monitor_order_list, done=len(need_monitor_order_list) == 0)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f'员工操作日志记录失败')
|
||||
return bool(need_monitor_order_list)
|
||||
|
||||
|
||||
@router.post("/bind_user", summary="用户绑定客户")
|
||||
@unified_resp
|
||||
async def bind_user(bind_info: WeixinUserBindInfo, login_user: WeixinUserDep):
|
||||
result = await weixin_customer_controller.bind_user(bind_info=bind_info)
|
||||
try:
|
||||
await action_controller.new_action(ActionType.BIND_USER, login_user, **bind_info.model_dump(exclude_unset=True))
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f'员工操作日志记录失败')
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/bind_inner_user", summary="用户绑定内部公司成员(erp和企微架构不同)")
|
||||
@unified_resp
|
||||
async def bind_inner_user(bind_info: WeixinUserBindInfo, login_user: WeixinUserDep):
|
||||
result = await weixin_user_controller.bind_user(bind_info)
|
||||
try:
|
||||
await action_controller.new_action(ActionType.BIND_USER, login_user, **bind_info.model_dump(exclude_unset=True))
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f'员工操作日志记录失败')
|
||||
return result
|
||||
|
||||
@router.get("/load_recent_user_from_xingyun", summary="加载最近N天的客户数据")
|
||||
@unified_resp
|
||||
async def load_user_from_xingyun(days_range: int = Query(3, description="加载最近N天的客户数据")):
|
||||
|
||||
now_time = datetime.now()
|
||||
start_time = (now_time - timedelta(days=days_range)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_time = now_time + timedelta(days=1)
|
||||
return await weixin_customer_controller.load_user_from_xingyun(add_time_start=start_time, add_time_end=end_time)
|
||||
|
||||
@router.get("/load_all_user_from_xingyun", summary="加载所有客户数据")
|
||||
@unified_resp
|
||||
async def load_all_user_from_xingyun(background_tasks: BackgroundTasks):
|
||||
await event_manager.publish_async(EventType.SYNC_XINGYUN_CONTACT_INFO, {}, background_tasks)
|
||||
return True
|
||||
|
||||
@router.post("/trigger_create_group_chat", summary="触发创建群聊")
|
||||
@unified_resp
|
||||
async def trigger_create_group_chat(group_chat: TriggerWeixinGroupChat, login_user: WeixinUserDep):
|
||||
try:
|
||||
await action_controller.new_action(ActionType.CREATE_GROUP, login_user, **group_chat.model_dump(exclude_unset=True))
|
||||
if group_chat.order_id:
|
||||
username = login_user.english_name or login_user.nickname
|
||||
username = username.split('-印刷')[0]
|
||||
remark = f'企微联系{username}拉群'
|
||||
need_monitor_order_list = await weixin_customer_controller.remark_order_by_order_id(remark=remark, order_id=group_chat.order_id)
|
||||
await action_controller.new_action(ActionType.WRITE_REMARK, login_user, order_id=group_chat.order_id, remark=remark, orders=need_monitor_order_list, done=len(need_monitor_order_list) == 0)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error(f'员工操作日志记录失败')
|
||||
return True
|
||||
Reference in New Issue
Block a user