first commit

This commit is contained in:
2026-06-25 17:41:06 +08:00
commit b2f23a933b
370 changed files with 30526 additions and 0 deletions
+29
View File
@@ -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])
+8
View File
@@ -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"]
+67
View File
@@ -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")
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .auditlog import router
auditlog_router = APIRouter()
auditlog_router.include_router(router, tags=["审计日志模块"])
__all__ = ["auditlog_router"]
+44
View File
@@ -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)
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .automation import router
automation_router = APIRouter()
automation_router.include_router(router, tags=["自动化模块"])
__all__ = ["automation_router"]
+188
View File
@@ -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()
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .base import router
base_router = APIRouter()
base_router.include_router(router, tags=["基础模块"])
__all__ = ["base_router"]
+103
View File
@@ -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="修改成功")
+10
View File
@@ -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"]
+73
View File
@@ -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")
+81
View File
@@ -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")
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .depts import router
depts_router = APIRouter()
depts_router.include_router(router, tags=["部门模块"])
__all__ = ["depts_router"]
+48
View File
@@ -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")
+8
View File
@@ -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"]
+95
View File
@@ -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)
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .menus import router
menus_router = APIRouter()
menus_router.include_router(router, tags=["菜单模块"])
__all__ = ["menus_router"]
+63
View File
@@ -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")
+9
View File
@@ -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"]
+15
View File
@@ -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}
+80
View File
@@ -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)
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .roles import router
roles_router = APIRouter()
roles_router.include_router(router, tags=["角色模块"])
__all__ = ["roles_router"]
+73
View File
@@ -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")
+8
View File
@@ -0,0 +1,8 @@
from fastapi import APIRouter
from .users import router
users_router = APIRouter()
users_router.include_router(router, tags=["用户模块"])
__all__ = ["users_router"]
+87
View File
@@ -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")
+11
View File
@@ -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"]
+16
View File
@@ -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]
+2
View File
@@ -0,0 +1,2 @@
httpx
pypinyin
+95
View File
@@ -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)
+294
View File
@@ -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