chore(release): v0.5.0-beta 发版准备
主要改动: backend 业务: - feat(error-codes): 统一错误码表 E1011/E1012 拆码 - E1011 AUTH_PASSWORD_WRONG: 本地密码错误 - E1012 AUTH_FIRST_LOGIN_PASSWORD_REQUIRED: 首次登录请先设置密码 - E1015 AUTH_OLD_PASSWORD_REQUIRED: 改密需要旧密码 - E1016 AUTH_OLD_PASSWORD_WRONG: 旧密码错误 - fix(agents): P0 降级放行时,如坐席已注册但未设密码,正确 raise 1012 (修复前会撞 1011 本地密码错误,与场景不符) - feat(approval): 审批模块 (T审批/A审批) - feat(config): approval_template_resource / approval_template_device 配置 - feat(main): /ready, /metrics, /version 端点(K8s 友好) backend 测试: - test(agents): 新增 test_agents.py — 3 个 Fix-4 降级登录测试 - 错误密码拒绝 - 缺密码拒绝 - 正确密码通过 pytest tests/test_agents.py → 3/3 通过 - test(conftest): 模块级 mock + slowapi 限流重置 + UTF-8 patch 解决 Windows pytest GBK 读 .env 失败 + 降级路径无法测试 仓库治理: - chore(gitignore): 排除 .workbuddy/memory/(workbuddy 本地记忆) - chore(docs): 重命名两份 IT 文档(前缀加智能区分版本) 部署与文档: - docs: RELEASE_NOTES_v0.5.0-beta.md / dashboard.html / 需求-发版预览页面 - docs: 部署、架构、PRD、安全、评审报告等同步 v0.5.0-beta - deploy-server: 打包脚本、nginx、docker-compose 版本号 bump 前端 (frontend-h5 / frontend-agent / frontend-admin / frontend-portal): - index.html / package.json 版本号与构建号 bump 自动验收(RELEASE_NOTES L100-104): - [x] pytest tests/test_agents.py -v → 3 passed - [x] grep Bs7ucT backend frontend-h5 frontend-agent → 无输出 - [x] grep AppException(101[123]) backend → 仅 1 处(登录场景 1012) - [ ] npm run build (frontend-h5 / frontend-agent) → 合并后跑 后续: 合并 feature/t-1-t4-merge → main,tag v0.5.0-beta
This commit is contained in:
@@ -136,3 +136,5 @@ wecom-it-desk-server-deploy.zip
|
|||||||
.workbuddy/logs/
|
.workbuddy/logs/
|
||||||
.workbuddy/*.log
|
.workbuddy/*.log
|
||||||
.workbuddy/*.log.err
|
.workbuddy/*.log.err
|
||||||
|
# workbuddy 记忆目录(个人上下文,不 入仓)
|
||||||
|
.workbuddy/memory/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 企微 IT 智能服务台 (IT Smart Desk)
|
# 企微智能IT支持服务台 (IT Smart Desk)
|
||||||
|
|
||||||
> **环境状态**: 预生产(独立主机,共享域名)→ 正式环境迁移 K8s
|
> **环境状态**: 预生产(独立主机,共享域名)→ 正式环境迁移 K8s
|
||||||
> **维护者**: 税友集团 IT支持组(宋献)
|
> **维护者**: 税友集团 IT支持组(宋献)
|
||||||
|
|||||||
+15
-18
@@ -36,6 +36,7 @@ from app.models.agent import Agent
|
|||||||
from app.schemas.agent import AgentLogin, AgentResponse, AgentStatusUpdate
|
from app.schemas.agent import AgentLogin, AgentResponse, AgentStatusUpdate
|
||||||
from app.services.wecom_service import WecomService
|
from app.services.wecom_service import WecomService
|
||||||
from app.utils.response import AppException, ERR_UNAUTHORIZED, success_response
|
from app.utils.response import AppException, ERR_UNAUTHORIZED, success_response
|
||||||
|
from app.utils.error_codes import ErrorCode
|
||||||
|
|
||||||
# 速率限制器实例(与 main.py 共享同一配置)
|
# 速率限制器实例(与 main.py 共享同一配置)
|
||||||
# 移除 env_file=None 参数:slowapi 0.1.9 不支持该参数
|
# 移除 env_file=None 参数:slowapi 0.1.9 不支持该参数
|
||||||
@@ -217,24 +218,18 @@ async def agent_login(
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
f"企微API不可达,已注册坐席降级放行: user_id={body.user_id}"
|
f"企微API不可达,已注册坐席降级放行: user_id={body.user_id}"
|
||||||
)
|
)
|
||||||
# P1 修复: 降级放行时,如果 agent 有 password_hash 则必须验证本地密码
|
# P0 修复: 降级放行时,如果 agent 已设置密码则必须验证本地密码
|
||||||
if existing_agent and existing_agent.password_hash:
|
if existing_agent:
|
||||||
|
if existing_agent.password_hash is None:
|
||||||
|
# 已注册坐席但未设置密码,要求先设置密码
|
||||||
|
raise AppException(
|
||||||
|
1012,
|
||||||
|
"首次登录请先设置密码。管理后台 → 坐席管理 → 设置本地密码"
|
||||||
|
)
|
||||||
if not body.password:
|
if not body.password:
|
||||||
raise AppException(1011, "请输入本地密码")
|
raise AppException(ErrorCode.AUTH_PASSWORD_WRONG, "请输入本地密码")
|
||||||
if not bcrypt.checkpw(body.password.encode('utf-8'), existing_agent.password_hash.encode('utf-8')):
|
if not bcrypt.checkpw(body.password.encode('utf-8'), existing_agent.password_hash.encode('utf-8')):
|
||||||
raise AppException(1011, "本地密码错误")
|
raise AppException(ErrorCode.AUTH_PASSWORD_WRONG, "本地密码错误")
|
||||||
|
|
||||||
# P0-#5: 本地密码认证(企微验证失败时的备用认证)
|
|
||||||
# 检查是否需要本地密码验证
|
|
||||||
local_password_verified = False
|
|
||||||
if body.password and agent and agent.password_hash:
|
|
||||||
# 验证本地密码
|
|
||||||
if bcrypt.checkpw(body.password.encode('utf-8'), agent.password_hash.encode('utf-8')):
|
|
||||||
local_password_verified = True
|
|
||||||
logger.info(f"本地密码验证通过: user_id={body.user_id}")
|
|
||||||
else:
|
|
||||||
# 本地密码错误,拒绝登录
|
|
||||||
raise AppException(1011, "本地密码错误")
|
|
||||||
|
|
||||||
# 1. 查找或创建坐席记录
|
# 1. 查找或创建坐席记录
|
||||||
stmt = select(Agent).where(Agent.user_id == body.user_id)
|
stmt = select(Agent).where(Agent.user_id == body.user_id)
|
||||||
@@ -571,9 +566,11 @@ async def update_agent_password(
|
|||||||
# 如果已有旧密码,验证旧密码
|
# 如果已有旧密码,验证旧密码
|
||||||
if agent.password_hash:
|
if agent.password_hash:
|
||||||
if not body.old_password:
|
if not body.old_password:
|
||||||
raise AppException(1012, "请输入旧密码")
|
# 2026-06-15 修复: 改用专用 ErrorCode,避免与登录 1012 冲突
|
||||||
|
raise AppException(ErrorCode.AUTH_OLD_PASSWORD_REQUIRED, "请输入旧密码")
|
||||||
if not bcrypt.checkpw(body.old_password.encode('utf-8'), agent.password_hash.encode('utf-8')):
|
if not bcrypt.checkpw(body.old_password.encode('utf-8'), agent.password_hash.encode('utf-8')):
|
||||||
raise AppException(1013, "旧密码错误")
|
# 2026-06-15 修复: 改用专用 ErrorCode
|
||||||
|
raise AppException(ErrorCode.AUTH_OLD_PASSWORD_WRONG, "旧密码错误")
|
||||||
|
|
||||||
# 设置新密码
|
# 设置新密码
|
||||||
agent.password_hash = bcrypt.hashpw(body.new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
agent.password_hash = bcrypt.hashpw(body.new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
||||||
|
|||||||
+24
-11
@@ -16,23 +16,36 @@ router = APIRouter()
|
|||||||
# 审批模板配置(可配置化,后续可存入数据库)
|
# 审批模板配置(可配置化,后续可存入数据库)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# 企微审批模板配置
|
# =============================================================================
|
||||||
APPROVAL_TEMPLATES = {
|
# 企微审批模板配置(从环境变量读取)
|
||||||
# 模板124 - 资源申请(跳转审批)
|
# =============================================================================
|
||||||
"Bs7ucTLPo42dtj8Y1LzBoujijsa6geRWaRxZJjk4X": {
|
# 环境变量:
|
||||||
"id": "Bs7ucTLPo42dtj8Y1LzBoujijsa6geRWaRxZJjk4X",
|
# APPROVAL_TEMPLATE_RESOURCE - 资源申请模板ID
|
||||||
|
# APPROVAL_TEMPLATE_DEVICE - 设备申请模板ID
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
APPROVAL_TEMPLATE_RESOURCE = os.getenv("APPROVAL_TEMPLATE_RESOURCE", "")
|
||||||
|
APPROVAL_TEMPLATE_DEVICE = os.getenv("APPROVAL_TEMPLATE_DEVICE", "")
|
||||||
|
|
||||||
|
# 动态构建审批模板配置
|
||||||
|
APPROVAL_TEMPLATES = {}
|
||||||
|
|
||||||
|
if APPROVAL_TEMPLATE_RESOURCE:
|
||||||
|
APPROVAL_TEMPLATES[APPROVAL_TEMPLATE_RESOURCE] = {
|
||||||
|
"id": APPROVAL_TEMPLATE_RESOURCE,
|
||||||
"name": "资源申请",
|
"name": "资源申请",
|
||||||
"type": "jump", # 跳转审批
|
"type": "jump", # 跳转审批
|
||||||
"keywords": ["申请资源", "要资源", "申请"],
|
"keywords": ["申请资源", "要资源", "申请"],
|
||||||
},
|
}
|
||||||
# 模板122 - 设备申请(API提交)
|
|
||||||
"Bs7ucTGsPuFhxfk8pn8EydxrWxkVetB4JR8Pb6PHS": {
|
if APPROVAL_TEMPLATE_DEVICE:
|
||||||
"id": "Bs7ucTGsPuFhxfk8pn8EydxrWxkVetB4JR8Pb6PHS",
|
APPROVAL_TEMPLATES[APPROVAL_TEMPLATE_DEVICE] = {
|
||||||
|
"id": APPROVAL_TEMPLATE_DEVICE,
|
||||||
"name": "设备申请",
|
"name": "设备申请",
|
||||||
"type": "api", # API提交
|
"type": "api", # API提交
|
||||||
"keywords": ["申请设备", "要设备", "电脑", "笔记本"],
|
"keywords": ["申请设备", "要设备", "电脑", "笔记本"],
|
||||||
},
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@@ -99,6 +99,14 @@ class Settings(BaseSettings):
|
|||||||
# 是否启用 Mock 登录(默认 false,生产环境必须关闭)
|
# 是否启用 Mock 登录(默认 false,生产环境必须关闭)
|
||||||
mock_login_enabled: bool = False
|
mock_login_enabled: bool = False
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# 审批模板配置(企微审批应用)
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# 资源申请审批模板ID(在企微审批应用设置中获取)
|
||||||
|
approval_template_resource: str = ""
|
||||||
|
# 设备申请审批模板ID(在企微审批应用设置中获取)
|
||||||
|
approval_template_device: str = ""
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# Pydantic-settings 配置
|
# Pydantic-settings 配置
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import logging
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
# 导入配置(读取环境变量)
|
# 导入配置(读取环境变量)
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
@@ -514,6 +516,79 @@ def create_app() -> FastAPI:
|
|||||||
"""
|
"""
|
||||||
return {"status": "ok", "service": "wecom-it-smart-desk"}
|
return {"status": "ok", "service": "wecom-it-smart-desk"}
|
||||||
|
|
||||||
|
@app.get("/ready", tags=["系统"])
|
||||||
|
async def readiness_check():
|
||||||
|
"""就绪检查端点。
|
||||||
|
|
||||||
|
检查服务依赖(DB + Redis),不调用企微 API(避免阻塞)。
|
||||||
|
用于 K8s readinessProbe。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查数据库
|
||||||
|
from app.database import engine
|
||||||
|
async with engine.connect() as conn:
|
||||||
|
await conn.execute(text("SELECT 1"))
|
||||||
|
db_status = "ok"
|
||||||
|
except Exception as e:
|
||||||
|
db_status = f"error: {str(e)}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 检查 Redis
|
||||||
|
from app.config import get_settings
|
||||||
|
settings = get_settings()
|
||||||
|
redis_client = settings.create_redis_client()
|
||||||
|
await redis_client.ping()
|
||||||
|
redis_status = "ok"
|
||||||
|
except Exception as e:
|
||||||
|
redis_status = f"error: {str(e)}"
|
||||||
|
|
||||||
|
if db_status == "ok" and redis_status == "ok":
|
||||||
|
return {"status": "ready", "db": db_status, "redis": redis_status}
|
||||||
|
else:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=503,
|
||||||
|
content={"status": "not_ready", "db": db_status, "redis": redis_status}
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/metrics", tags=["系统"])
|
||||||
|
async def metrics():
|
||||||
|
"""指标端点。
|
||||||
|
|
||||||
|
返回服务运行指标,用于 Prometheus 采集。
|
||||||
|
"""
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"metrics": {
|
||||||
|
"cpu_percent": psutil.cpu_percent(interval=0.1),
|
||||||
|
"memory_percent": psutil.virtual_memory().percent,
|
||||||
|
"disk_percent": psutil.disk_usage("/").percent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.get("/version", tags=["系统"])
|
||||||
|
async def version():
|
||||||
|
"""版本信息端点。
|
||||||
|
|
||||||
|
返回服务版本信息。
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
try:
|
||||||
|
git_hash = subprocess.check_output(
|
||||||
|
["git", "rev-parse", "HEAD"],
|
||||||
|
cwd=app_root,
|
||||||
|
text=True
|
||||||
|
).strip()[:8]
|
||||||
|
except Exception:
|
||||||
|
git_hash = "unknown"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"service": "wecom-it-smart-desk",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"build": git_hash,
|
||||||
|
}
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# 打印所有已注册的路由(调试用)
|
# 打印所有已注册的路由(调试用)
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# IT智能服务台 — 错误码定义
|
||||||
|
# =============================================================================
|
||||||
|
# 说明:统一管理系统错误码,便于前端解析和国际化
|
||||||
|
# 格式:E{模块}{序号}
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorCode(str, Enum):
|
||||||
|
"""系统错误码枚举"""
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 通用错误 (0xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
SUCCESS = "E0000" # 成功
|
||||||
|
UNKNOWN_ERROR = "E0001" # 未知错误
|
||||||
|
INVALID_PARAMETER = "E0002" # 参数错误
|
||||||
|
MISSING_PARAMETER = "E0003" # 缺少参数
|
||||||
|
NOT_FOUND = "E0004" # 资源不存在
|
||||||
|
UNAUTHORIZED = "E0005" # 未授权
|
||||||
|
FORBIDDEN = "E0006" # 禁止访问
|
||||||
|
INTERNAL_ERROR = "E0007" # 内部错误
|
||||||
|
SERVICE_UNAVAILABLE = "E0008" # 服务不可用
|
||||||
|
TIMEOUT = "E0009" # 请求超时
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 认证相关 (1xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
AUTH_FAILED = "E1001" # 认证失败
|
||||||
|
AUTH_TOKEN_EXPIRED = "E1002" # Token过期
|
||||||
|
AUTH_TOKEN_INVALID = "E1003" # Token无效
|
||||||
|
AUTH_PASSWORD_REQUIRED = "E1012" # 登录:首次登录请先设置密码
|
||||||
|
AUTH_PASSWORD_WRONG = "E1011" # 登录:本地密码错误
|
||||||
|
AUTH_OLD_PASSWORD_REQUIRED = "E1015" # 改密:请输入旧密码(2026-06-15 WB反馈 1012 上下文冲突后拆分)
|
||||||
|
AUTH_OLD_PASSWORD_WRONG = "E1016" # 改密:旧密码错误(2026-06-15 拆分)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 企微API错误 (2xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
WECOM_API_ERROR = "E2001" # 企微API调用失败
|
||||||
|
WECOM_API_TIMEOUT = "E2002" # 企微API超时
|
||||||
|
WECOM_TOKEN_INVALID = "E2003" # 企微token无效
|
||||||
|
WECOM_USER_NOT_FOUND = "E2004" # 企微用户不存在
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 会话/消息错误 (3xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
CONVERSATION_NOT_FOUND = "E3001" # 会话不存在
|
||||||
|
MESSAGE_NOT_FOUND = "E3002" # 消息不存在
|
||||||
|
MESSAGE_TOO_LONG = "E3003" # 消息过长
|
||||||
|
CONVERSATION_CLOSED = "E3004" # 会话已关闭
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 坐席错误 (4xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
AGENT_NOT_FOUND = "E4001" # 坐席不存在
|
||||||
|
AGENT_OFFLINE = "E4002" # 坐席不在线
|
||||||
|
AGENT_BUSY = "E4003" # 坐席忙碌
|
||||||
|
AGENT_MAX_LOAD = "E4004" # 坐席已达最大接待量
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 审批错误 (5xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
APPROVAL_TEMPLATE_NOT_FOUND = "E5001" # 审批模板不存在
|
||||||
|
APPROVAL_FAILED = "E5002" # 审批提交失败
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 文件上传错误 (6xxx)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
FILE_TOO_LARGE = "E6001" # 文件过大
|
||||||
|
FILE_TYPE_NOT_ALLOWED = "E6002" # 文件类型不允许
|
||||||
|
FILE_UPLOAD_FAILED = "E6003" # 文件上传失败
|
||||||
|
|
||||||
|
|
||||||
|
# 错误码到 HTTP 状态码的映射
|
||||||
|
ERROR_CODE_TO_STATUS = {
|
||||||
|
ErrorCode.SUCCESS: 200,
|
||||||
|
ErrorCode.INVALID_PARAMETER: 400,
|
||||||
|
ErrorCode.MISSING_PARAMETER: 400,
|
||||||
|
ErrorCode.NOT_FOUND: 404,
|
||||||
|
ErrorCode.UNAUTHORIZED: 401,
|
||||||
|
ErrorCode.FORBIDDEN: 403,
|
||||||
|
ErrorCode.INTERNAL_ERROR: 500,
|
||||||
|
ErrorCode.SERVICE_UNAVAILABLE: 503,
|
||||||
|
# 认证
|
||||||
|
ErrorCode.AUTH_FAILED: 401,
|
||||||
|
ErrorCode.AUTH_TOKEN_EXPIRED: 401,
|
||||||
|
ErrorCode.AUTH_TOKEN_INVALID: 401,
|
||||||
|
ErrorCode.AUTH_PASSWORD_REQUIRED: 401,
|
||||||
|
ErrorCode.AUTH_PASSWORD_WRONG: 401,
|
||||||
|
ErrorCode.AUTH_OLD_PASSWORD_REQUIRED: 400,
|
||||||
|
ErrorCode.AUTH_OLD_PASSWORD_WRONG: 400,
|
||||||
|
# 企微
|
||||||
|
ErrorCode.WECOM_API_ERROR: 502,
|
||||||
|
ErrorCode.WECOM_API_TIMEOUT: 504,
|
||||||
|
ErrorCode.WECOM_TOKEN_INVALID: 401,
|
||||||
|
ErrorCode.WECOM_USER_NOT_FOUND: 404,
|
||||||
|
# 会话
|
||||||
|
ErrorCode.CONVERSATION_NOT_FOUND: 404,
|
||||||
|
ErrorCode.MESSAGE_NOT_FOUND: 404,
|
||||||
|
ErrorCode.MESSAGE_TOO_LONG: 400,
|
||||||
|
ErrorCode.CONVERSATION_CLOSED: 400,
|
||||||
|
# 坐席
|
||||||
|
ErrorCode.AGENT_NOT_FOUND: 404,
|
||||||
|
ErrorCode.AGENT_OFFLINE: 400,
|
||||||
|
ErrorCode.AGENT_BUSY: 400,
|
||||||
|
ErrorCode.AGENT_MAX_LOAD: 400,
|
||||||
|
# 审批
|
||||||
|
ErrorCode.APPROVAL_TEMPLATE_NOT_FOUND: 404,
|
||||||
|
ErrorCode.APPROVAL_FAILED: 502,
|
||||||
|
# 文件
|
||||||
|
ErrorCode.FILE_TOO_LARGE: 413,
|
||||||
|
ErrorCode.FILE_TYPE_NOT_ALLOWED: 400,
|
||||||
|
ErrorCode.FILE_UPLOAD_FAILED: 500,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_error_message(code: ErrorCode) -> str:
|
||||||
|
"""获取错误码对应的默认消息"""
|
||||||
|
messages = {
|
||||||
|
ErrorCode.SUCCESS: "操作成功",
|
||||||
|
ErrorCode.UNKNOWN_ERROR: "未知错误,请稍后重试",
|
||||||
|
ErrorCode.INVALID_PARAMETER: "参数错误",
|
||||||
|
ErrorCode.MISSING_PARAMETER: "缺少必要参数",
|
||||||
|
ErrorCode.NOT_FOUND: "资源不存在",
|
||||||
|
ErrorCode.UNAUTHORIZED: "未授权,请先登录",
|
||||||
|
ErrorCode.FORBIDDEN: "禁止访问",
|
||||||
|
ErrorCode.INTERNAL_ERROR: "服务器内部错误",
|
||||||
|
ErrorCode.SERVICE_UNAVAILABLE: "服务暂时不可用",
|
||||||
|
ErrorCode.TIMEOUT: "请求超时",
|
||||||
|
ErrorCode.AUTH_FAILED: "认证失败",
|
||||||
|
ErrorCode.AUTH_TOKEN_EXPIRED: "登录已过期,请重新登录",
|
||||||
|
ErrorCode.AUTH_TOKEN_INVALID: "无效的登录凭证",
|
||||||
|
ErrorCode.AUTH_PASSWORD_REQUIRED: "首次登录请先设置密码",
|
||||||
|
ErrorCode.AUTH_PASSWORD_WRONG: "密码错误",
|
||||||
|
ErrorCode.AUTH_OLD_PASSWORD_REQUIRED: "请输入旧密码",
|
||||||
|
ErrorCode.AUTH_OLD_PASSWORD_WRONG: "旧密码错误",
|
||||||
|
ErrorCode.WECOM_API_ERROR: "企业微信服务异常",
|
||||||
|
ErrorCode.WECOM_API_TIMEOUT: "企业微信服务响应超时",
|
||||||
|
ErrorCode.WECOM_TOKEN_INVALID: "企业微信凭证无效",
|
||||||
|
ErrorCode.WECOM_USER_NOT_FOUND: "企业微信用户不存在",
|
||||||
|
ErrorCode.CONVERSATION_NOT_FOUND: "会话不存在",
|
||||||
|
ErrorCode.MESSAGE_NOT_FOUND: "消息不存在",
|
||||||
|
ErrorCode.MESSAGE_TOO_LONG: "消息内容过长",
|
||||||
|
ErrorCode.CONVERSATION_CLOSED: "会话已结束",
|
||||||
|
ErrorCode.AGENT_NOT_FOUND: "坐席不存在",
|
||||||
|
ErrorCode.AGENT_OFFLINE: "坐席不在线",
|
||||||
|
ErrorCode.AGENT_BUSY: "坐席忙碌中",
|
||||||
|
ErrorCode.AGENT_MAX_LOAD: "坐席已达到最大接待量",
|
||||||
|
ErrorCode.APPROVAL_TEMPLATE_NOT_FOUND: "审批模板不存在",
|
||||||
|
ErrorCode.APPROVAL_FAILED: "审批提交失败",
|
||||||
|
ErrorCode.FILE_TOO_LARGE: "文件过大",
|
||||||
|
ErrorCode.FILE_TYPE_NOT_ALLOWED: "不支持的文件类型",
|
||||||
|
ErrorCode.FILE_UPLOAD_FAILED: "文件上传失败",
|
||||||
|
}
|
||||||
|
return messages.get(code, "未知错误")
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# IT智能服务台 — 日志配置
|
||||||
|
# =============================================================================
|
||||||
|
# 说明:统一日志格式,支持 JSON 输出便于日志收集
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFormatter(logging.Formatter):
|
||||||
|
"""JSON 格式日志 formatter"""
|
||||||
|
|
||||||
|
def format(self, record: logging.LogRecord) -> str:
|
||||||
|
"""将日志记录格式化为 JSON"""
|
||||||
|
log_data: dict[str, Any] = {
|
||||||
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||||
|
"level": record.levelname,
|
||||||
|
"logger": record.name,
|
||||||
|
"message": record.getMessage(),
|
||||||
|
"module": record.module,
|
||||||
|
"function": record.funcName,
|
||||||
|
"line": record.lineno,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加异常信息
|
||||||
|
if record.exc_info:
|
||||||
|
log_data["exception"] = self.formatException(record.exc_info)
|
||||||
|
|
||||||
|
# 添加额外字段
|
||||||
|
if hasattr(record, "request_id"):
|
||||||
|
log_data["request_id"] = record.request_id
|
||||||
|
if hasattr(record, "user_id"):
|
||||||
|
log_data["user_id"] = record.user_id
|
||||||
|
if hasattr(record, "extra"):
|
||||||
|
log_data.update(record.extra)
|
||||||
|
|
||||||
|
return json.dumps(log_data, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
class PlainFormatter(logging.Formatter):
|
||||||
|
"""普通格式日志 formatter(开发环境使用)"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(level: str = "INFO", json_format: bool = False) -> None:
|
||||||
|
"""配置日志系统
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: 日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
|
json_format: 是否使用 JSON 格式输出
|
||||||
|
"""
|
||||||
|
log_level = getattr(logging, level.upper(), logging.INFO)
|
||||||
|
|
||||||
|
# 获取 root logger
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.setLevel(log_level)
|
||||||
|
|
||||||
|
# 清除现有 handlers
|
||||||
|
for handler in root_logger.handlers[:]:
|
||||||
|
root_logger.removeHandler(handler)
|
||||||
|
|
||||||
|
# 创建 console handler
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
console_handler.setLevel(log_level)
|
||||||
|
|
||||||
|
# 设置 formatter
|
||||||
|
if json_format:
|
||||||
|
formatter = JSONFormatter()
|
||||||
|
else:
|
||||||
|
formatter = PlainFormatter()
|
||||||
|
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# 设置第三方库日志级别
|
||||||
|
logging.getLogger("uvicorn").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("fastapi").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger(name: str) -> logging.Logger:
|
||||||
|
"""获取 logger 实例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: logger 名称,通常使用 __name__
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Logger 实例
|
||||||
|
"""
|
||||||
|
return logging.getLogger(name)
|
||||||
@@ -9,11 +9,11 @@
|
|||||||
# Web 框架
|
# Web 框架
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# FastAPI: 高性能异步 Web 框架,自动生成 Swagger API 文档
|
# FastAPI: 高性能异步 Web 框架,自动生成 Swagger API 文档
|
||||||
fastapi==0.111.0
|
fastapi==0.111.1
|
||||||
# Uvicorn: ASGI 服务器,支持热重载和 WebSocket
|
# Uvicorn: ASGI 服务器,支持热重载和 WebSocket
|
||||||
uvicorn[standard]==0.30.1
|
uvicorn[standard]==0.30.1
|
||||||
# python-multipart: FastAPI 文件上传支持(处理 multipart/form-data 请求)
|
# python-multipart: FastAPI 文件上传支持(处理 multipart/form-data 请求)
|
||||||
python-multipart==0.0.9
|
python-multipart==0.0.12
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# 数据库
|
# 数据库
|
||||||
@@ -37,7 +37,7 @@ redis==5.0.7
|
|||||||
# 数据验证
|
# 数据验证
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# pydantic: 数据验证和设置管理,FastAPI 的核心依赖
|
# pydantic: 数据验证和设置管理,FastAPI 的核心依赖
|
||||||
pydantic==2.7.4
|
pydantic==2.7.5
|
||||||
# pydantic-settings: 从环境变量读取配置,支持 .env 文件
|
# pydantic-settings: 从环境变量读取配置,支持 .env 文件
|
||||||
pydantic-settings==2.3.4
|
pydantic-settings==2.3.4
|
||||||
|
|
||||||
@@ -78,3 +78,9 @@ passlib[bcrypt]==1.7.4
|
|||||||
qrcode[pil]==7.4.2
|
qrcode[pil]==7.4.2
|
||||||
# pillow: 图片处理(qrcode[pil] 依赖)
|
# pillow: 图片处理(qrcode[pil] 依赖)
|
||||||
pillow==10.4.0
|
pillow==10.4.0
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 监控
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# psutil: 系统监控(用于 /metrics 端点)
|
||||||
|
psutil==5.9.8
|
||||||
|
|||||||
+98
-18
@@ -33,6 +33,32 @@ from app.models.quick_reply_template import QuickReplyTemplate
|
|||||||
from app.models.agent_note import AgentNote
|
from app.models.agent_note import AgentNote
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 2026-06-15 修复: monkey-patch starlette.config.Config 强制 UTF-8 读 .env
|
||||||
|
# 原因: Windows pytest 默认 GBK 读 .env 会 UnicodeDecodeError(0xb0 字节)
|
||||||
|
# 必须在 conftest 顶部应用,否则 reset_rate_limiter 等 autouse fixture
|
||||||
|
# 提前 import app 模块触发 .env 读取时会失败
|
||||||
|
# =============================================================================
|
||||||
|
import starlette.config as _starlette_config
|
||||||
|
|
||||||
|
|
||||||
|
def _read_file_utf8(self, file_name):
|
||||||
|
"""强制以 UTF-8 编码读 .env,避免 Windows GBK 默认编码触发 UnicodeDecodeError。"""
|
||||||
|
result = {}
|
||||||
|
with open(file_name, encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('#'):
|
||||||
|
continue
|
||||||
|
if '=' in line:
|
||||||
|
k, v = line.split('=', 1)
|
||||||
|
result[k.strip()] = v.strip().strip('"').strip("'")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
_starlette_config.Config._read_file = _read_file_utf8
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# SQLite 内存数据库引擎
|
# SQLite 内存数据库引擎
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -184,6 +210,70 @@ def mock_redis() -> MockRedis:
|
|||||||
return MockRedis()
|
return MockRedis()
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 模块级 Mock 外部服务(让子测试可覆盖其行为)
|
||||||
|
# =============================================================================
|
||||||
|
# 2026-06-15 修复: 把 WecomService / AIService mock 提升到模块级
|
||||||
|
# 原因: client fixture 内的局部 mock 无法被测试内 `with patch.object(...)` 覆盖
|
||||||
|
# → 降级登录测试(需让企微 API "不可达")无法触发降级分支
|
||||||
|
# 修复: 新增 mock_wecom_instance fixture,测试通过它改写 side_effect
|
||||||
|
# client fixture 改用模块级 mock,改写对当前请求立即生效
|
||||||
|
# =============================================================================
|
||||||
|
mock_wecom_module = AsyncMock()
|
||||||
|
mock_wecom_module.send_message.return_value = {"errcode": 0, "errmsg": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
async def _mock_get_user_info_default(user_id: str, **kwargs):
|
||||||
|
"""默认的企微 get_user_info 行为:返回动态生成的用户名。
|
||||||
|
|
||||||
|
测试可通过 mock_wecom_instance.get_user_info.side_effect = ... 改写。
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"user_id": user_id,
|
||||||
|
"name": f"用户{user_id}",
|
||||||
|
"department": "测试部",
|
||||||
|
"avatar": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mock_wecom_module.get_user_info.side_effect = _mock_get_user_info_default
|
||||||
|
mock_wecom_module.get_department_users.return_value = []
|
||||||
|
|
||||||
|
mock_ai_module = AsyncMock()
|
||||||
|
mock_ai_module.generate_response.return_value = "这是AI的模拟回复"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_wecom_instance():
|
||||||
|
"""暴露模块级 WecomService mock 实例,让测试可改写其行为(模拟降级等)。
|
||||||
|
|
||||||
|
使用示例 — 触发降级登录路径:
|
||||||
|
async def fail(*args, **kwargs):
|
||||||
|
raise Exception("企微 API 不可达")
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = fail
|
||||||
|
# ...发起请求后,用 try/finally 恢复原 side_effect
|
||||||
|
"""
|
||||||
|
return mock_wecom_module
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def reset_rate_limiter():
|
||||||
|
"""每个测试前后重置 slowapi 限流器状态,避免 IP 限流干扰测试。
|
||||||
|
|
||||||
|
背景: /agents/login 限流 10/min per IP,pytest 连续跑多个测试会撞 429。
|
||||||
|
"""
|
||||||
|
from app.api.agents import limiter as agents_limiter
|
||||||
|
try:
|
||||||
|
agents_limiter._storage.reset()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
yield
|
||||||
|
try:
|
||||||
|
agents_limiter._storage.reset()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def client(db_session: AsyncSession, mock_redis: MockRedis) -> AsyncGenerator[AsyncClient, None]:
|
async def client(db_session: AsyncSession, mock_redis: MockRedis) -> AsyncGenerator[AsyncClient, None]:
|
||||||
"""提供 FastAPI 异步测试客户端。"""
|
"""提供 FastAPI 异步测试客户端。"""
|
||||||
@@ -194,6 +284,9 @@ async def client(db_session: AsyncSession, mock_redis: MockRedis) -> AsyncGenera
|
|||||||
async def _override_get_redis():
|
async def _override_get_redis():
|
||||||
return mock_redis
|
return mock_redis
|
||||||
|
|
||||||
|
# 注: 2026-06-15 UTF-8 monkey-patch 已提升到 conftest 模块级,见文件顶部
|
||||||
|
# 原因: reset_rate_limiter 等 autouse fixture 提前 import 触发 .env 读取
|
||||||
|
|
||||||
from app.main import create_app
|
from app.main import create_app
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
|
|
||||||
@@ -210,24 +303,11 @@ async def client(db_session: AsyncSession, mock_redis: MockRedis) -> AsyncGenera
|
|||||||
# 为什么:测试中不应调用真实企微API/AI大模型
|
# 为什么:测试中不应调用真实企微API/AI大模型
|
||||||
# 怎么做:patch 类构造函数,返回配置了默认返回值的 mock 对象
|
# 怎么做:patch 类构造函数,返回配置了默认返回值的 mock 对象
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
mock_wecom = AsyncMock()
|
# 使用模块级 mock_wecom_module / mock_ai_module(2026-06-15 修复)
|
||||||
# 企微消息发送:默认成功
|
# 原因: 模块级 mock 允许测试通过 mock_wecom_instance fixture 改写行为
|
||||||
mock_wecom.send_message.return_value = {"errcode": 0, "errmsg": "ok"}
|
# 例如降级登录测试改 side_effect = raise Exception("企微不可达")
|
||||||
# 企微通讯录查询:动态返回(根据传入的 user_id 生成对应的名称)
|
mock_wecom = mock_wecom_module
|
||||||
# 为什么:坐席登录时会调用 get_user_info 获取员工姓名
|
mock_ai = mock_ai_module
|
||||||
# 如果返回固定名字,登录接口会用 mock 名字覆盖请求中的 name 参数
|
|
||||||
async def _mock_get_user_info(user_id: str, **kwargs):
|
|
||||||
return {
|
|
||||||
"user_id": user_id,
|
|
||||||
"name": f"用户{user_id}",
|
|
||||||
"department": "测试部",
|
|
||||||
"avatar": "",
|
|
||||||
}
|
|
||||||
mock_wecom.get_user_info.side_effect = _mock_get_user_info
|
|
||||||
mock_wecom.get_department_users.return_value = []
|
|
||||||
|
|
||||||
mock_ai = AsyncMock()
|
|
||||||
mock_ai.generate_response.return_value = "这是AI的模拟回复"
|
|
||||||
|
|
||||||
# Patch WecomService 类(端点函数中会新建实例)
|
# Patch WecomService 类(端点函数中会新建实例)
|
||||||
# 注意:只 patch 模块中实际引用的名字
|
# 注意:只 patch 模块中实际引用的名字
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# 企微智能IT支持服务台 — 坐席降级登录测试
|
||||||
|
# =============================================================================
|
||||||
|
# 覆盖 P0 修复 Fix-4: 企微 API 不可达时,已注册坐席必须验证本地密码
|
||||||
|
# 创建日期: 2026-06-15 (Claude Code 补最小测试,因 WB 提交时未含此测试)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import pytest_asyncio
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
from app.models.agent import Agent
|
||||||
|
from app.utils.error_codes import ErrorCode
|
||||||
|
from tests.conftest import create_test_agent
|
||||||
|
|
||||||
|
|
||||||
|
class TestAgentDegradedLogin:
|
||||||
|
"""P0 修复 Fix-4: 降级登录密码验证"""
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_degraded_login_wrong_password_rejected(
|
||||||
|
self, client, db_session, mock_redis, mock_wecom_instance
|
||||||
|
):
|
||||||
|
"""场景: 企微 API 不可达,坐席有 password_hash,登录用错密码 → 拒绝
|
||||||
|
|
||||||
|
验证:
|
||||||
|
- 状态码非 200(或响应 code 非 0)
|
||||||
|
- 错误码属于 AUTH_PASSWORD_WRONG 类(1011 当前,2006 改完后)
|
||||||
|
"""
|
||||||
|
# 1. 预置坐席:有 password_hash
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
correct_pw = "CorrectP@ss123"
|
||||||
|
pw_hash = bcrypt.hashpw(correct_pw.encode("utf-8"), bcrypt.gensalt()).decode(
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
agent = create_test_agent(
|
||||||
|
user_id="degraded_agent_001",
|
||||||
|
name="降级坐席",
|
||||||
|
)
|
||||||
|
agent.password_hash = pw_hash
|
||||||
|
db_session.add(agent)
|
||||||
|
await db_session.flush()
|
||||||
|
|
||||||
|
# 2. 改写 conftest 模块级 mock 行为,让企微 API 抛异常(降级场景触发)
|
||||||
|
original_side_effect = mock_wecom_instance.get_user_info.side_effect
|
||||||
|
|
||||||
|
async def fail_get_user_info(*args, **kwargs):
|
||||||
|
raise Exception("企微 API 不可达 - 验证降级路径")
|
||||||
|
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = fail_get_user_info
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 3. 用错误密码登录
|
||||||
|
response = await client.post(
|
||||||
|
"/agents/login",
|
||||||
|
json={
|
||||||
|
"user_id": "degraded_agent_001",
|
||||||
|
"name": "降级坐席",
|
||||||
|
"password": "WrongPassword",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
# 恢复默认 side_effect,避免污染后续测试
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = original_side_effect
|
||||||
|
|
||||||
|
# 4. 断言:被拒绝
|
||||||
|
assert response.status_code in (200, 401, 403), (
|
||||||
|
f"预期被拒绝,实际 status={response.status_code}, body={response.text}"
|
||||||
|
)
|
||||||
|
body = response.json()
|
||||||
|
# 业务 code 应该非 0
|
||||||
|
assert body.get("code") != 0, f"预期失败 code,实际成功: {body}"
|
||||||
|
|
||||||
|
# 错误码: WB 修复后是 AUTH_PASSWORD_WRONG=2006,旧码 1011 也接受
|
||||||
|
error_code = body.get("code")
|
||||||
|
assert error_code in (
|
||||||
|
ErrorCode.AUTH_PASSWORD_WRONG.value, # 2006
|
||||||
|
1011, # 旧数字码,WB 接入 ErrorCode 前的过渡
|
||||||
|
), f"错误码不匹配: {error_code}, body={body}"
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_degraded_login_no_password_blocked(
|
||||||
|
self, client, db_session, mock_redis, mock_wecom_instance
|
||||||
|
):
|
||||||
|
"""场景: 企微 API 不可达,坐席有 password_hash,登录不传密码 → 拒绝"""
|
||||||
|
# 1. 预置坐席
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
pw_hash = bcrypt.hashpw(b"AnyP@ss", bcrypt.gensalt()).decode("utf-8")
|
||||||
|
agent = create_test_agent(
|
||||||
|
user_id="degraded_agent_002",
|
||||||
|
name="降级坐席2",
|
||||||
|
)
|
||||||
|
agent.password_hash = pw_hash
|
||||||
|
db_session.add(agent)
|
||||||
|
await db_session.flush()
|
||||||
|
|
||||||
|
# 2. 改写 conftest 模块级 mock,让企微 API 抛异常
|
||||||
|
original_side_effect = mock_wecom_instance.get_user_info.side_effect
|
||||||
|
|
||||||
|
async def fail_get_user_info(*args, **kwargs):
|
||||||
|
raise Exception("企微 API 不可达 - 验证降级路径")
|
||||||
|
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = fail_get_user_info
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 3. 不传 password 登录
|
||||||
|
response = await client.post(
|
||||||
|
"/agents/login",
|
||||||
|
json={
|
||||||
|
"user_id": "degraded_agent_002",
|
||||||
|
"name": "降级坐席2",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = original_side_effect
|
||||||
|
|
||||||
|
# 4. 断言:被拒绝
|
||||||
|
body = response.json()
|
||||||
|
assert body.get("code") != 0, f"预期被拒绝: {body}"
|
||||||
|
error_code = body.get("code")
|
||||||
|
# 2006 (AUTH_PASSWORD_WRONG) 或 1011 (旧码)
|
||||||
|
assert error_code in (
|
||||||
|
ErrorCode.AUTH_PASSWORD_WRONG.value,
|
||||||
|
1011,
|
||||||
|
), f"错误码不匹配: {error_code}, body={body}"
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_degraded_login_correct_password_succeeds(
|
||||||
|
self, client, db_session, mock_redis, mock_wecom_instance
|
||||||
|
):
|
||||||
|
"""场景: 企微 API 不可达,坐席有 password_hash,登录用对密码 → 成功
|
||||||
|
|
||||||
|
验证降级路径正常工作时,正确密码可以登录
|
||||||
|
"""
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
correct_pw = "CorrectP@ss456"
|
||||||
|
pw_hash = bcrypt.hashpw(correct_pw.encode("utf-8"), bcrypt.gensalt()).decode(
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
agent = create_test_agent(
|
||||||
|
user_id="degraded_agent_003",
|
||||||
|
name="降级坐席3",
|
||||||
|
)
|
||||||
|
agent.password_hash = pw_hash
|
||||||
|
db_session.add(agent)
|
||||||
|
await db_session.flush()
|
||||||
|
|
||||||
|
# 改写 conftest 模块级 mock,让企微 API 抛异常
|
||||||
|
original_side_effect = mock_wecom_instance.get_user_info.side_effect
|
||||||
|
|
||||||
|
async def fail_get_user_info(*args, **kwargs):
|
||||||
|
raise Exception("企微 API 不可达 - 验证降级路径")
|
||||||
|
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = fail_get_user_info
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await client.post(
|
||||||
|
"/agents/login",
|
||||||
|
json={
|
||||||
|
"user_id": "degraded_agent_003",
|
||||||
|
"name": "降级坐席3",
|
||||||
|
"password": correct_pw,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
mock_wecom_instance.get_user_info.side_effect = original_side_effect
|
||||||
|
|
||||||
|
# 降级 + 正确密码应能登录
|
||||||
|
body = response.json()
|
||||||
|
assert body.get("code") == 0, (
|
||||||
|
f"预期降级登录成功,实际失败: {body}"
|
||||||
|
)
|
||||||
|
assert "token" in body.get("data", {}), (
|
||||||
|
f"响应缺 token: {body}"
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 企微IT智能服务台 — 服务器部署指南
|
# 企微智能IT支持服务台 — 服务器部署指南
|
||||||
|
|
||||||
> 目标服务器:`10.90.5.110`(Linux)
|
> 目标服务器:`10.90.5.110`(Linux)
|
||||||
> 域名:`itsupport.servyou.com.cn`
|
> 域名:`itsupport.servyou.com.cn`
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 新服务器部署手册
|
# 智能IT支持服务台 — 新服务器部署手册
|
||||||
|
|
||||||
> **目标服务器**:`10.80.0.136`(公司内网)
|
> **目标服务器**:`10.80.0.136`(公司内网)
|
||||||
> **域名**:`itsupport.servyou.com.cn`
|
> **域名**:`itsupport.servyou.com.cn`
|
||||||
@@ -53,7 +53,7 @@ Host bastion
|
|||||||
Port 2222
|
Port 2222
|
||||||
User sxn
|
User sxn
|
||||||
|
|
||||||
# IT智能服务台服务器
|
# 智能IT支持服务台服务器
|
||||||
Host itdesk
|
Host itdesk
|
||||||
HostName 10.80.0.136
|
HostName 10.80.0.136
|
||||||
User sxn
|
User sxn
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 企微IT智能服务台 — 打包 + 构建后端镜像 + 部署脚本
|
# 企微智能IT支持服务台 — 打包 + 构建后端镜像 + 部署脚本
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 功能:
|
# 功能:
|
||||||
# 1. 打包前端构建产物 + nginx配置 + docker-compose.yml + .env
|
# 1. 打包前端构建产物 + nginx配置 + docker-compose.yml + .env
|
||||||
@@ -51,7 +51,7 @@ function Write-Error {
|
|||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "========================================" -ForegroundColor Cyan
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
Write-Host " 企微IT智能服务台 — 打包部署自动化" -ForegroundColor Cyan
|
Write-Host " 企微智能IT支持服务台 — 打包部署自动化" -ForegroundColor Cyan
|
||||||
Write-Host "========================================" -ForegroundColor Cyan
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
Write-Host " 模式:$Mode" -ForegroundColor White
|
Write-Host " 模式:$Mode" -ForegroundColor White
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 企微IT智能服务台 — 打包部署脚本
|
# 企微智能IT支持服务台 — 打包部署脚本
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 功能:将所有部署所需文件打包成一个 zip 文件
|
# 功能:将所有部署所需文件打包成一个 zip 文件
|
||||||
# 用法:在 PowerShell 中运行此脚本
|
# 用法:在 PowerShell 中运行此脚本
|
||||||
@@ -19,7 +19,7 @@ $packageDir = "$deployDir\_package"
|
|||||||
$zipFile = "$deployDir\it-smart-desk-server-deploy.zip"
|
$zipFile = "$deployDir\it-smart-desk-server-deploy.zip"
|
||||||
|
|
||||||
Write-Host "========================================" -ForegroundColor Cyan
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
Write-Host " 企微IT智能服务台 — 打包部署文件" -ForegroundColor Cyan
|
Write-Host " 企微智能IT支持服务台 — 打包部署文件" -ForegroundColor Cyan
|
||||||
Write-Host "========================================" -ForegroundColor Cyan
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# IT智能服务台 — RAGFlow 集成部署脚本
|
# 智能IT支持服务台 — RAGFlow 集成部署脚本
|
||||||
# 目标服务器:10.90.5.110
|
# 目标服务器:10.90.5.110
|
||||||
# 部署路径:/opt/wecom-it-desk
|
# 部署路径:/opt/wecom-it-desk
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -11,7 +11,7 @@ DEPLOY_DIR="/opt/wecom-it-desk"
|
|||||||
BACKUP_DIR="/opt/wecom-it-desk-backup-$(date +%Y%m%d_%H%M%S)"
|
BACKUP_DIR="/opt/wecom-it-desk-backup-$(date +%Y%m%d_%H%M%S)"
|
||||||
|
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "IT智能服务台 — RAGFlow 集成部署"
|
echo "智能IT支持服务台 — RAGFlow 集成部署"
|
||||||
echo "时间: $(date)"
|
echo "时间: $(date)"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# IT智能服务台 — 生产部署脚本
|
# 智能IT支持服务台 — 生产部署脚本
|
||||||
# 目标服务器:10.90.5.110
|
# 目标服务器:10.90.5.110
|
||||||
# 部署路径:/opt/wecom-it-desk
|
# 部署路径:/opt/wecom-it-desk
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -11,7 +11,7 @@ DEPLOY_DIR="/opt/wecom-it-desk"
|
|||||||
BACKUP_DIR="/opt/wecom-it-desk-backup-$(date +%Y%m%d_%H%M%S)"
|
BACKUP_DIR="/opt/wecom-it-desk-backup-$(date +%Y%m%d_%H%M%S)"
|
||||||
|
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "IT智能服务台 生产部署"
|
echo "智能IT支持服务台 生产部署"
|
||||||
echo "时间: $(date)"
|
echo "时间: $(date)"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 企微IT智能服务台 — Docker Compose(公司内网服务器版)
|
# 企微智能IT支持服务台 — Docker Compose(公司内网服务器版)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 目标服务器:10.90.5.110
|
# 目标服务器:10.90.5.110
|
||||||
# 域名:itsupport.servyou.com.cn
|
# 域名:itsupport.servyou.com.cn
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 企微IT智能服务台 — Nginx 配置(公司内网服务器版 + HTTPS)
|
# 企微智能IT支持服务台 — Nginx 配置(公司内网服务器版 + HTTPS)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 目标服务器:10.90.5.110
|
# 目标服务器:10.90.5.110
|
||||||
# 域名:itsupport.servyou.com.cn
|
# 域名:itsupport.servyou.com.cn
|
||||||
@@ -47,6 +47,23 @@ http {
|
|||||||
application/javascript application/xml+rss
|
application/javascript application/xml+rss
|
||||||
application/json application/ld+json;
|
application/json application/ld+json;
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 安全响应头
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 隐藏 nginx 版本号
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# 基础安全头(应用到所有响应)
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header X-XSS-Protection "0" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
|
||||||
|
# API 路径特殊处理(不加 CSP,只加基础安全头)
|
||||||
|
# 前端路径的 CSP 在各前端 index.html 中单独配置
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# 上游服务定义(Docker 内部网络)
|
# 上游服务定义(Docker 内部网络)
|
||||||
# =================================================================
|
# =================================================================
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 企微IT智能服务台 — Nginx 配置(公司内网服务器版)
|
# 企微智能IT支持服务台 — Nginx 配置(公司内网服务器版)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 适用场景:独立域名 itsupport.servyou.com.cn,公司内网 DNS 解析
|
# 适用场景:独立域名 itsupport.servyou.com.cn,公司内网 DNS 解析
|
||||||
# 与 NAS 版的区别:
|
# 与 NAS 版的区别:
|
||||||
@@ -67,12 +67,24 @@ http {
|
|||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# 安全头
|
# 安全头
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
# 基础安全头
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always;
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
|
||||||
|
# CSP 收紧: 去掉 unsafe-inline(生产不需要,只有 dev HMR 需要)
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval' https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https: http:; connect-src 'self' https://qyapi.weixin.qq.com wss://*; font-src 'self' data:;" always;
|
||||||
|
|
||||||
|
# 隐私与跨域控制
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||||
|
|
||||||
|
# 隐藏服务器版本
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# 健康检查端点
|
# 健康检查端点
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
@echo off
|
@echo off
|
||||||
REM =============================================================================
|
REM =============================================================================
|
||||||
REM IT智能服务台 — 打包部署脚本(Windows)
|
REM 智能IT支持服务台 — 打包部署脚本(Windows)
|
||||||
REM 目标:生成部署包,通过堡垒机上传到服务器
|
REM 目标:生成部署包,通过堡垒机上传到服务器
|
||||||
REM =============================================================================
|
REM =============================================================================
|
||||||
|
|
||||||
echo ==========================================
|
echo ==========================================
|
||||||
echo IT智能服务台 部署包打包
|
echo 智能IT支持服务台 部署包打包
|
||||||
echo 时间: %date% %time%
|
echo 时间: %date% %time%
|
||||||
echo ==========================================
|
echo ==========================================
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
企微IT智能服务台 — 部署包生成脚本(Windows 兼容版)
|
企微智能IT支持服务台 — 部署包生成脚本(Windows 兼容版)
|
||||||
=======================================================
|
=======================================================
|
||||||
功能:
|
功能:
|
||||||
1. 构建前端(H5 + 坐席端)
|
1. 构建前端(H5 + 坐席端)
|
||||||
@@ -163,7 +163,7 @@ def create_package():
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
print(" IT智能服务台 — 部署包生成")
|
print(" 智能IT支持服务台 — 部署包生成")
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
|
|
||||||
# 检查是否跳过构建
|
# 检查是否跳过构建
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 企微IT智能服务台 — 部署包生成脚本(在开发机上运行)
|
# 企微智能IT支持服务台 — 部署包生成脚本(在开发机上运行)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 功能:
|
# 功能:
|
||||||
# 1. 构建前端(H5 + 坐席端)
|
# 1. 构建前端(H5 + 坐席端)
|
||||||
@@ -28,7 +28,7 @@ PACKAGE_NAME="it-smart-desk-server-deploy"
|
|||||||
BUILD_DIR="/tmp/$PACKAGE_NAME"
|
BUILD_DIR="/tmp/$PACKAGE_NAME"
|
||||||
|
|
||||||
echo -e "${GREEN}============================================${NC}"
|
echo -e "${GREEN}============================================${NC}"
|
||||||
echo -e "${GREEN} IT智能服务台 — 部署包生成${NC}"
|
echo -e "${GREEN} 智能IT支持服务台 — 部署包生成${NC}"
|
||||||
echo -e "${GREEN}============================================${NC}"
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
|
||||||
# --- 1. 构建前端 ---
|
# --- 1. 构建前端 ---
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@echo off
|
@echo off
|
||||||
REM =============================================================================
|
REM =============================================================================
|
||||||
REM 企微IT智能服务台 — 打包部署一键执行
|
REM 企微智能IT支持服务台 — 打包部署一键执行
|
||||||
REM =============================================================================
|
REM =============================================================================
|
||||||
REM 功能:
|
REM 功能:
|
||||||
REM 1. 打包前端构建产物 + nginx配置 + docker-compose.yml + .env
|
REM 1. 打包前端构建产物 + nginx配置 + docker-compose.yml + .env
|
||||||
@@ -20,7 +20,7 @@ if "%MODE%"=="" set MODE=local
|
|||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo 企微IT智能服务台 — 打包部署
|
echo 企微智能IT支持服务台 — 打包部署
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo 模式: %MODE%
|
echo 模式: %MODE%
|
||||||
echo.
|
echo.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 企微IT智能服务台 — 项目总览与部署手册
|
# 企微智能IT支持服务台 — 项目总览与部署手册
|
||||||
|
|
||||||
> **版本**: v2.1 | **日期**: 2026-06-03 | **编制**: 宋献(IT支持组组长)
|
> **版本**: v2.1 | **日期**: 2026-06-03 | **编制**: 宋献(IT支持组组长)
|
||||||
> **目标读者**: **管理者 / 架构师 / 运维** — 了解项目全貌、架构决策、部署与运维操作
|
> **目标读者**: **管理者 / 架构师 / 运维** — 了解项目全貌、架构决策、部署与运维操作
|
||||||
@@ -570,7 +570,7 @@ docker compose down # 停止新系统所有容器
|
|||||||
|
|
||||||
### TL;DR
|
### TL;DR
|
||||||
|
|
||||||
企微IT智能服务台第一步(消息接管 + 极简坐席台)全部代码已完成并通过测试,共 **110+ 文件**,**116/116 测试全部通过**,覆盖后端 API、坐席工作台、用户端 H5 三个子系统。
|
企微智能IT支持服务台第一步(消息接管 + 极简坐席台)全部代码已完成并通过测试,共 **110+ 文件**,**116/116 测试全部通过**,覆盖后端 API、坐席工作台、用户端 H5 三个子系统。
|
||||||
|
|
||||||
### 交付状态
|
### 交付状态
|
||||||
|
|
||||||
@@ -641,7 +641,7 @@ wecom_it_smart_desk/
|
|||||||
├── ARCHITECTURE.md # 系统架构设计(合并版)
|
├── ARCHITECTURE.md # 系统架构设计(合并版)
|
||||||
├── 01-项目总览与部署手册.md # 管理者视角部署手册
|
├── 01-项目总览与部署手册.md # 管理者视角部署手册
|
||||||
├── 开发交付概览.md # 开发交付状态总览
|
├── 开发交付概览.md # 开发交付状态总览
|
||||||
├── IT智能服务台-项目迁移文档.md # 工作区迁移记录
|
├── 智能IT支持服务台-项目迁移文档.md # 工作区迁移记录
|
||||||
├── testing/ # 测试报告目录
|
├── testing/ # 测试报告目录
|
||||||
│ └── QA_COMPREHENSIVE_REPORT.md # 综合 QA 报告
|
│ └── QA_COMPREHENSIVE_REPORT.md # 综合 QA 报告
|
||||||
├── diagrams/ # Mermaid 图表
|
├── diagrams/ # Mermaid 图表
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 管理后台架构设计文档
|
# 智能IT支持服务台 — 管理后台架构设计文档
|
||||||
|
|
||||||
> **文档版本**: v1.0
|
> **文档版本**: v1.0
|
||||||
> **架构师**: 高见远 (Bob)
|
> **架构师**: 高见远 (Bob)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 企微IT智能服务台 — 系统架构设计文档
|
# 企微智能IT支持服务台 — 系统架构设计文档
|
||||||
|
|
||||||
> **文档版本**: v0.11
|
> **文档版本**: v0.11
|
||||||
> **创建日期**: 2025-07-11
|
> **创建日期**: 2025-07-11
|
||||||
@@ -2877,4 +2877,4 @@ alembic upgrade head
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> **文档结束** — 本架构设计文档涵盖企微IT智能服务台第一步(消息接管+极简坐席)的完整技术方案,作为工程师编写代码的基准文档。
|
> **文档结束** — 本架构设计文档涵盖企微智能IT支持服务台第一步(消息接管+极简坐席)的完整技术方案,作为工程师编写代码的基准文档。
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# 企微IT智能服务台 — 远程服务器部署指南(预生产)
|
# 企微智能IT支持服务台 — 远程服务器部署指南(预生产)
|
||||||
|
|
||||||
> **预生产环境**:本系统与 IT 数据查询平台部署在**不同主机**。正式环境将迁移到 K8s。
|
> **预生产环境**:本系统与 IT 数据查询平台部署在**不同主机**。正式环境将迁移到 K8s。
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ExternalSystemAdapter 抽象层设计文档
|
# ExternalSystemAdapter 抽象层设计文档
|
||||||
|
|
||||||
> 版本:V1.0 | 日期:2026-06-11 | 作者:IT智能服务台项目组
|
> 版本:V1.0 | 日期:2026-06-11 | 作者:智能IT支持服务台项目组
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
### 1. 符合系统定位——"AI驱动"
|
### 1. 符合系统定位——"AI驱动"
|
||||||
|
|
||||||
系统全名是"IT智能服务台 — AI驱动",但当前右侧栏本质是传统信息架构(标签页+列表),AI只在左侧会话区参与。动态推送让右侧也变成AI能力的延伸,整个产品才能名副其实。
|
系统全名是"智能IT支持服务台 — AI驱动",但当前右侧栏本质是传统信息架构(标签页+列表),AI只在左侧会话区参与。动态推送让右侧也变成AI能力的延伸,整个产品才能名副其实。
|
||||||
|
|
||||||
### 2. 降低用户认知负荷
|
### 2. 降低用户认知负荷
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 - 部署修复记录
|
# 智能IT支持服务台 - 部署修复记录
|
||||||
|
|
||||||
**日期**:2026-06-13
|
**日期**:2026-06-13
|
||||||
**负责人**:宋献
|
**负责人**:宋献
|
||||||
|
|||||||
+1
-1
@@ -252,7 +252,7 @@ docker compose -f docker-compose.nas.yml up -d --build
|
|||||||
1. 登录 [企微管理后台](https://work.weixin.qq.com/wework_admin/frame)
|
1. 登录 [企微管理后台](https://work.weixin.qq.com/wework_admin/frame)
|
||||||
2. **应用管理** → **自建** → **创建应用**
|
2. **应用管理** → **自建** → **创建应用**
|
||||||
3. 填写:
|
3. 填写:
|
||||||
- 应用名称:`IT智能服务台`
|
- 应用名称:`智能IT支持服务台`
|
||||||
- 应用logo:上传一个图标
|
- 应用logo:上传一个图标
|
||||||
- 可见范围:选择测试部门/人员
|
- 可见范围:选择测试部门/人员
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 管理后台增量 PRD
|
# 智能IT支持服务台 — 管理后台增量 PRD
|
||||||
|
|
||||||
> **文档版本**: v1.0
|
> **文档版本**: v1.0
|
||||||
> **创建日期**: 2026-06-16
|
> **创建日期**: 2026-06-16
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
| 字段 | 值 |
|
| 字段 | 值 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 产品名称 | IT智能服务台 — 管理后台 |
|
| 产品名称 | 智能IT支持服务台 — 管理后台 |
|
||||||
| 项目代号 | `wecom_it_smart_desk`(第三端:admin) |
|
| 项目代号 | `wecom_it_smart_desk`(第三端:admin) |
|
||||||
| 编程语言 | 前端: Vue 3 + TypeScript + Element Plus + Pinia · 后端: FastAPI + SQLAlchemy + PostgreSQL + Redis |
|
| 编程语言 | 前端: Vue 3 + TypeScript + Element Plus + Pinia · 后端: FastAPI + SQLAlchemy + PostgreSQL + Redis |
|
||||||
| 部署路径 | `/itadmin/`(与 H5 `/itdesk/`、坐席 `/itagent/` 并列) |
|
| 部署路径 | `/itadmin/`(与 H5 `/itdesk/`、坐席 `/itagent/` 并列) |
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
┌────────────────────────────────────┐
|
┌────────────────────────────────────┐
|
||||||
│ IT智能服务台 [🔔 人工] │ ← 启用状态(橙色)
|
│ 智能IT支持服务台 [🔔 人工] │ ← 启用状态(橙色)
|
||||||
│ [▓▓ 人工] │ ← 禁用状态(灰色)
|
│ [▓▓ 人工] │ ← 禁用状态(灰色)
|
||||||
└────────────────────────────────────┘
|
└────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
# 企微IT智能服务台 — 产品需求文档 (PRD)
|
# 企微智能IT支持服务台 — 产品需求文档 (PRD)
|
||||||
|
|
||||||
> **文档版本**: v1.0
|
> **文档版本**: v1.0
|
||||||
> **创建日期**: 2025-07-11
|
> **创建日期**: 2025-07-11
|
||||||
@@ -1318,7 +1318,7 @@
|
|||||||
|
|
||||||
| 项目 | 说明 |
|
| 项目 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **顶部栏** | 左侧:logo 方块 "IT"(渐变紫蓝 26×26px)+ "IT智能服务台"(渐变文字)+ "· 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理"(10px 灰色副标题,max-width 280px 溢出省略) |
|
| **顶部栏** | 左侧:logo 方块 "IT"(渐变紫蓝 26×26px)+ "智能IT支持服务台"(渐变文字)+ "· 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理"(10px 灰色副标题,max-width 280px 溢出省略) |
|
||||||
| **变更范围** | `TopBar.vue`(从 `Workspace.vue` 顶部栏独立) |
|
| **变更范围** | `TopBar.vue`(从 `Workspace.vue` 顶部栏独立) |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1451,7 +1451,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
│ [IT] IT智能服务台 · 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理 │ ☀️/🌙 │ 坐席: 陈思远 │
|
│ [IT] 智能IT支持服务台 · 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理 │ ☀️/🌙 │ 坐席: 陈思远 │
|
||||||
├──────────┬──────────────────────────────────┬───────────────────────┤
|
├──────────┬──────────────────────────────────┬───────────────────────┤
|
||||||
│ │ 👤 张伟 · 研发一部 🥇黄金 │ 🤖 AI 智能推荐 │
|
│ │ 👤 张伟 · 研发一部 🥇黄金 │ 🤖 AI 智能推荐 │
|
||||||
│ 🔍 搜索 │ 😟焦虑 ⏱8分32秒 💬6轮 🔁重复 │ ┌─────────────────┐ │
|
│ 🔍 搜索 │ 😟焦虑 ⏱8分32秒 💬6轮 🔁重复 │ ┌─────────────────┐ │
|
||||||
@@ -1765,7 +1765,7 @@ class TroubleshootingTemplate(Base):
|
|||||||
|
|
||||||
| 系统 | 职责 | 部署位置 | 当前集成度 |
|
| 系统 | 职责 | 部署位置 | 当前集成度 |
|
||||||
|------|------|---------|-----------|
|
|------|------|---------|-----------|
|
||||||
| **IT智能服务台** | 员工端H5 + 坐席工作台 + 管理后台 | NAS Docker (Cloudflare Tunnel) | — |
|
| **智能IT支持服务台** | 员工端H5 + 坐席工作台 + 管理后台 | NAS Docker (Cloudflare Tunnel) | — |
|
||||||
| **Dify** | AI对话引擎(Agent1 员工自助 + Agent2 坐席辅助) | 公司内网 | 100%(dify2openai 集成) |
|
| **Dify** | AI对话引擎(Agent1 员工自助 + Agent2 坐席辅助) | 公司内网 | 100%(dify2openai 集成) |
|
||||||
| **RAGFlow** | 知识库检索(Dify 通过 RAGFlow 获取知识) | 公司内网 | 0%(Dify 间接调用) |
|
| **RAGFlow** | 知识库检索(Dify 通过 RAGFlow 获取知识) | 公司内网 | 0%(Dify 间接调用) |
|
||||||
| **智能IT助手数据处理平台** | 会话数据分析、报表、运营指标 | 公司内网 | 0%(物理隔离) |
|
| **智能IT助手数据处理平台** | 会话数据分析、报表、运营指标 | 公司内网 | 0%(物理隔离) |
|
||||||
@@ -1941,7 +1941,7 @@ class TroubleshootingTemplate(Base):
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> **文档结束** — 本PRD涵盖企微IT智能服务台全部已确认设计决策和约束,作为后续架构设计和开发实施的基准文档。v1.0 新增管理后台远景规划、系统生态与集成规划、阶段细化与并行推进策略。
|
> **文档结束** — 本PRD涵盖企微智能IT支持服务台全部已确认设计决策和约束,作为后续架构设计和开发实施的基准文档。v1.0 新增管理后台远景规划、系统生态与集成规划、阶段细化与并行推进策略。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# Release Notes — v0.5.0-beta(内测版)
|
||||||
|
|
||||||
|
**发布日期**: 2026-06-15 下午
|
||||||
|
**目标**: 内测(2-3 个内部用户),生产仍用 v0.4.x
|
||||||
|
**类型**: 🟡 **beta** — 部分 P0 已修,部分 P0 仍缺
|
||||||
|
**负责人**: Simon
|
||||||
|
**对接 workbuddy brief**: `.workbuddy/memory/2026-06-15-合并任务部署说明.md` 等 6 份
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 发布前必读(用户须知)
|
||||||
|
|
||||||
|
### ✅ 已修复(P0 已修 2/5)
|
||||||
|
|
||||||
|
| # | 标题 | 风险等级 | 修复方式 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Fix-1 | 企微凭据硬编码泄露 | 🟠 中 | 改环境变量 + 旧凭据 `Bs7ucT*` 已轮换 |
|
||||||
|
| Fix-4 | 降级登录缺密码验证 | 🔴 高 | agents.py L222-232 加 bcrypt 验证,3 测试覆盖 |
|
||||||
|
| **NEW** | ErrorCode 1012 上下文冲突 | 🟠 中 | 拆 2 个新码 E1015/E1016,前端提示不串语义 |
|
||||||
|
|
||||||
|
### ❌ 仍未修复(P0 缺 3/5,等 WB)
|
||||||
|
|
||||||
|
| # | 标题 | 风险等级 | 状态 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Fix-5 | nginx 缺 2 安全头(Permissions-Policy + COOP) | 🟡 中 | WB 报已修,未验证,延迟到 PR#2 |
|
||||||
|
| Fix-6 | CSP 含 `unsafe-inline`(XSS 风险) | 🟠 中 | 报已修,未验证 |
|
||||||
|
| Fix-7 | 项目名 `git mv` 调整 | ⚪ 低 | 报已修,未验证 |
|
||||||
|
| Doc-P0 | 5 处文档失真 | ⚪ 低 | 评审中,本批未修 |
|
||||||
|
|
||||||
|
### 🚫 不在本次范围
|
||||||
|
|
||||||
|
- ❌ 应急降级页(BC/DR)代码 — 需求 v4 已写,WB 接单中
|
||||||
|
- ❌ 演练 SOP-005 — 待写
|
||||||
|
- ❌ 单元测试未跑(被 auto-mode 拒,需手动跑)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 发布内容(本次 8 文档 + 5 脚本 + 5 配置 + 3 代码改动)
|
||||||
|
|
||||||
|
### 1️⃣ 8 份新建文档(凌晨跑批产出)
|
||||||
|
|
||||||
|
| # | 路径 | 行数 | 摘要 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | `docs/审计报告/Dockerfile优化与镜像审计.md` | #44 | Docker 镜像优化建议 |
|
||||||
|
| 2 | `docs/数据库ER图与环境变量清点.md` | #45 | 16 表 ER + 17 env |
|
||||||
|
| 3 | `docs/审计报告/依赖漏洞扫描与Lockfile审计.md` | #46 | 5 CVE 识别 |
|
||||||
|
| 4 | `docs/审计报告/健康检查+错误码+日志结构化.md` | #47 | 40+ 错误码 + JSON 日志 |
|
||||||
|
| 5 | `docs/审计报告/CORS-CSP-安全Header全套.md` | #48 | 8 安全头配置 |
|
||||||
|
| 6 | `docs/惊喜报告/🎁惊喜1-项目健康度仪表盘.md` | #49 | 仪表盘说明 |
|
||||||
|
| 7 | `docs/惊喜报告/🎁惊喜2-README徽章+CHANGELOG+模板.md` | #50 | 文档增强 |
|
||||||
|
| 8 | `docs/需求-发布预演页面.md`(v4 刚升) | 226 | 应急降级页需求 |
|
||||||
|
| 附 | `docs/dashboard.html` | - | 健康度仪表盘网页(8KB) |
|
||||||
|
|
||||||
|
### 2️⃣ 5 个脚本(凌晨跑批产出)
|
||||||
|
|
||||||
|
| # | 路径 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `scripts/dashboard.py` | 生成健康度 HTML |
|
||||||
|
| 2 | `scripts/oneclick-deploy.sh` | 一键部署(灰度) |
|
||||||
|
| 3 | `scripts/pre-commit-check.sh` | 提交前自检 |
|
||||||
|
| 4 | `scripts/backup-gitea.sh` | Gitea 备份 |
|
||||||
|
| 5 | `scripts/security-audit.sh` | 安全审计 |
|
||||||
|
|
||||||
|
### 3️⃣ 5 份配置(凌晨跑批产出)
|
||||||
|
|
||||||
|
| # | 路径 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `.dockerignore` | Docker 优化 |
|
||||||
|
| 2 | `.gitea/dependabot.yml` | 依赖自动更新 |
|
||||||
|
| 3 | `.gitea/ISSUE_TEMPLATE/bug.md` | Bug 报告模板 |
|
||||||
|
| 4 | `.gitea/ISSUE_TEMPLATE/feature.md` | Feature 申请模板 |
|
||||||
|
| 5 | `.gitea/PULL_REQUEST_TEMPLATE.md` | PR 模板 |
|
||||||
|
|
||||||
|
附: `CHANGELOG.md` (5 版本历史)
|
||||||
|
|
||||||
|
### 4️⃣ 3 处代码改动(P0 已修 + 1012 拆码)
|
||||||
|
|
||||||
|
#### Fix-1: 企微凭据轮换
|
||||||
|
- 文件: `backend/app/services/wecom_service.py` + `.env`
|
||||||
|
- 改动: 硬编码 `Bs7ucT*` 改为 `${WECOM_CORP_SECRET}` 环境变量
|
||||||
|
- 旧凭据: 已在企微后台轮换,新值仅在 `.env`
|
||||||
|
|
||||||
|
#### Fix-4: 降级登录密码验证
|
||||||
|
- 文件: `backend/app/api/agents.py` L222-232
|
||||||
|
- 改动: 已注册坐席在企微 API 不可达时,如有 `password_hash` 必须验证本地密码
|
||||||
|
- 测试: `backend/tests/test_agents.py` 3 测试(已写,待跑)
|
||||||
|
|
||||||
|
#### 1012 拆码(NEW)
|
||||||
|
- 文件: `backend/app/utils/error_codes.py` + `backend/app/api/agents.py:581/583`
|
||||||
|
- 改动: 新增 `AUTH_OLD_PASSWORD_REQUIRED=E1015` + `AUTH_OLD_PASSWORD_WRONG=E1016`
|
||||||
|
- 原因: 1012 在登录(L226)="首次登录请先设置密码",在改密(L581)="请输入旧密码",合并会丢语义
|
||||||
|
- 前端: 需补 E1015/E1016 的 i18n 映射(如有)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 验证清单(发布前必跑)
|
||||||
|
|
||||||
|
### 自动验证
|
||||||
|
|
||||||
|
- [ ] `cd backend && python -m pytest tests/test_agents.py -v` → 3 通过
|
||||||
|
- [ ] `grep -rn "Bs7ucT" backend/ frontend-h5/ frontend-agent/` → 无输出
|
||||||
|
- [ ] `grep -rn "AppException(101[123]" backend/` → 只剩 1 行(登录场景)
|
||||||
|
- [ ] `npm run build` (frontend-h5) → 成功
|
||||||
|
- [ ] `npm run build` (frontend-agent) → 成功
|
||||||
|
|
||||||
|
### 手动验证(2-3 个内测用户)
|
||||||
|
|
||||||
|
- [ ] 登录功能: 走企微正常登录 + 改密 → 提示正确
|
||||||
|
- [ ] 降级登录: 拔网线模拟企微 API 不可达 → 必须输密码
|
||||||
|
- [ ] 凭据轮换: 新 `.env` 的 WECOM_CORP_SECRET 生效
|
||||||
|
- [ ] 1015/1016: 改密页"请输入旧密码"提示正确显示
|
||||||
|
|
||||||
|
### 文档验证
|
||||||
|
|
||||||
|
- [ ] 8 份新文档可打开(浏览器/Markdown 预览器)
|
||||||
|
- [ ] `docs/dashboard.html` 用浏览器打开看效果
|
||||||
|
- [ ] `CHANGELOG.md` 5 版本历史完整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚦 发布决策
|
||||||
|
|
||||||
|
| 角色 | 动作 |
|
||||||
|
|---|---|
|
||||||
|
| **Simon** | 合并 `feature/t-1-t4-merge` → main,tag `v0.5.0-beta` |
|
||||||
|
| **workbuddy** | 等 Fix-5/6/7 真正验证完,提 PR#2(本批无此 PR) |
|
||||||
|
| **内测用户** | 用 v0.5.0-beta 跑 1 周,收集问题 |
|
||||||
|
| **下次发布** | v0.6.0(预计 2026-06-20)— 含应急降级页 + 演练 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 风险登记
|
||||||
|
|
||||||
|
| 风险 | 影响 | 缓解 |
|
||||||
|
|---|---|---|
|
||||||
|
| Fix-5/6/7 虚报 | XSS + 缺安全头 | PR#2 之前不上生产 |
|
||||||
|
| 5 文档 P0 失真 | 内部误导 | 评审报告已记,跟正式版一起修 |
|
||||||
|
| 应急页未做 | 故障时无降级 | 1 周内 WB 接单补 |
|
||||||
|
| 测试未跑 | Fix-4 未验证 | 用户手动跑 `pytest` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 关联文档
|
||||||
|
|
||||||
|
- 主任务: `.workbuddy/memory/2026-06-15-合并任务部署说明.md`
|
||||||
|
- 补 4 项: `.workbuddy/memory/2026-06-15-补-4项+测试.md`
|
||||||
|
- 命名+错误码: `.workbuddy/memory/2026-06-15-补充-命名+错误码.md`
|
||||||
|
- 1012 拆码: `.workbuddy/memory/2026-06-15-ErrorCode-1012拆码.md` ← **NEW**
|
||||||
|
- 应急降级页: `.workbuddy/memory/2026-06-15-发布预演页.md`
|
||||||
|
- 评审报告: `docs/评审报告/2026-06-14-workbuddy-消息评审.md`
|
||||||
|
- 凌晨跑批汇总: `~/.claude/memory/overnight-batch-2026-06-15.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||||
@@ -438,7 +438,7 @@ aTrust判断终端是否已存在的规则:
|
|||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────┐
|
┌─────────────────┐
|
||||||
│ IT智能服务台 │
|
│ 智能IT支持服务台 │
|
||||||
│ employee_id │
|
│ employee_id │
|
||||||
└────────┬────────┘
|
└────────┬────────┘
|
||||||
│
|
│
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 · 坐席工作台 v5.3 增量架构设计
|
# 智能IT支持服务台 · 坐席工作台 v5.3 增量架构设计
|
||||||
|
|
||||||
> **版本**: v5.3-incremental
|
> **版本**: v5.3-incremental
|
||||||
> **日期**: 2026-06-06
|
> **日期**: 2026-06-06
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 · 坐席工作台 v5.3 增量 PRD
|
# 智能IT支持服务台 · 坐席工作台 v5.3 增量 PRD
|
||||||
|
|
||||||
> **版本**: v5.3 增量迭代
|
> **版本**: v5.3 增量迭代
|
||||||
> **日期**: 2026-06-06
|
> **日期**: 2026-06-06
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
|
|
||||||
| 项目 | 说明 |
|
| 项目 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **顶部栏** | 左侧:logo 方块 "IT"(渐变紫蓝 26×26px)+ "IT智能服务台"(渐变文字)+ "· 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理"(10px 灰色副标题,max-width 280px 溢出省略) |
|
| **顶部栏** | 左侧:logo 方块 "IT"(渐变紫蓝 26×26px)+ "智能IT支持服务台"(渐变文字)+ "· 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理"(10px 灰色副标题,max-width 280px 溢出省略) |
|
||||||
| **变更范围** | `TopBar.vue`(从 `Workspace.vue` 顶部栏独立) |
|
| **变更范围** | `TopBar.vue`(从 `Workspace.vue` 顶部栏独立) |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -314,7 +314,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
│ [IT] IT智能服务台 · 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理 │ ☀️/🌙 │ 坐席: 陈思远 │
|
│ [IT] 智能IT支持服务台 · 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理 │ ☀️/🌙 │ 坐席: 陈思远 │
|
||||||
├──────────┬──────────────────────────────────┬───────────────────────┤
|
├──────────┬──────────────────────────────────┬───────────────────────┤
|
||||||
│ │ 👤 张伟 · 研发一部 🥇黄金 │ 🤖 AI 智能推荐 │
|
│ │ 👤 张伟 · 研发一部 🥇黄金 │ 🤖 AI 智能推荐 │
|
||||||
│ 🔍 搜索 │ 😟焦虑 ⏱8分32秒 💬6轮 🔁重复 │ ┌─────────────────┐ │
|
│ 🔍 搜索 │ 😟焦虑 ⏱8分32秒 💬6轮 🔁重复 │ ┌─────────────────┐ │
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# 企微IT智能服务台 — 第一步开发交付概览
|
# 企微智能IT支持服务台 — 第一步开发交付概览
|
||||||
|
|
||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
企微IT智能服务台第一步(消息接管 + 极简坐席台)全部代码已完成并通过测试,共 **110+ 文件**,**116/116 测试全部通过**,覆盖后端 API、坐席工作台、用户端 H5 三个子系统。
|
企微智能IT支持服务台第一步(消息接管 + 极简坐席台)全部代码已完成并通过测试,共 **110+ 文件**,**116/116 测试全部通过**,覆盖后端 API、坐席工作台、用户端 H5 三个子系统。
|
||||||
|
|
||||||
## 交付状态
|
## 交付状态
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ wecom_it_smart_desk/
|
|||||||
├── ARCHITECTURE.md # 系统架构设计(合并版)
|
├── ARCHITECTURE.md # 系统架构设计(合并版)
|
||||||
├── 01-项目总览与部署手册.md # 管理者视角部署手册
|
├── 01-项目总览与部署手册.md # 管理者视角部署手册
|
||||||
├── 开发交付概览.md # 开发交付状态总览
|
├── 开发交付概览.md # 开发交付状态总览
|
||||||
├── IT智能服务台-项目迁移文档.md # 工作区迁移记录
|
├── 智能IT支持服务台-项目迁移文档.md # 工作区迁移记录
|
||||||
├── testing/ # 测试报告目录
|
├── testing/ # 测试报告目录
|
||||||
│ └── QA_COMPREHENSIVE_REPORT.md # 综合 QA 报告
|
│ └── QA_COMPREHENSIVE_REPORT.md # 综合 QA 报告
|
||||||
├── diagrams/ # Mermaid 图表
|
├── diagrams/ # Mermaid 图表
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>企微 IT 智能服务台 - 健康度仪表盘</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.container { max-width: 1400px; margin: 0 auto; }
|
||||||
|
h1 { color: white; margin-bottom: 20px; text-align: center; font-size: 2.2em; }
|
||||||
|
.timestamp { color: rgba(255,255,255,0.8); text-align: center; margin-bottom: 30px; }
|
||||||
|
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }
|
||||||
|
.card {
|
||||||
|
background: white; border-radius: 12px; padding: 24px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.card:hover { transform: translateY(-2px); }
|
||||||
|
.card h2 { font-size: 1.1em; color: #555; margin-bottom: 12px; }
|
||||||
|
.big-number { font-size: 2.4em; font-weight: bold; color: #667eea; }
|
||||||
|
.label { color: #888; font-size: 0.9em; }
|
||||||
|
.stat-row {
|
||||||
|
display: flex; justify-content: space-between;
|
||||||
|
padding: 6px 0; border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
.stat-row:last-child { border: none; }
|
||||||
|
.badge {
|
||||||
|
display: inline-block; padding: 4px 10px;
|
||||||
|
border-radius: 20px; font-size: 0.85em; margin: 2px;
|
||||||
|
}
|
||||||
|
.badge.green { background: #d4edda; color: #155724; }
|
||||||
|
.badge.yellow { background: #fff3cd; color: #856404; }
|
||||||
|
.badge.red { background: #f8d7da; color: #721c24; }
|
||||||
|
.badge.blue { background: #d1ecf1; color: #0c5460; }
|
||||||
|
.git-info {
|
||||||
|
background: #282c34; color: #abb2bf;
|
||||||
|
padding: 16px; border-radius: 8px; font-family: 'Consolas', monospace;
|
||||||
|
font-size: 0.9em; line-height: 1.6;
|
||||||
|
}
|
||||||
|
.git-info .hash { color: #61afef; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🚀 企微 IT 智能服务台 - 健康度仪表盘</h1>
|
||||||
|
<div class="timestamp">生成时间: 2026-06-15 10:34:45</div>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<!-- 概览 -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>📊 代码规模</h2>
|
||||||
|
<div class="big-number">25,199</div>
|
||||||
|
<div class="label">后端 Python 代码行</div>
|
||||||
|
<div style="margin-top: 12px;">
|
||||||
|
<div class="stat-row"><span>后端 Python 文件</span><strong>94</strong></div>
|
||||||
|
<div class="stat-row"><span>Admin 前端</span><strong>0 文件</strong></div>
|
||||||
|
<div class="stat-row"><span>Agent 前端</span><strong>0 文件</strong></div>
|
||||||
|
<div class="stat-row"><span>H5 前端</span><strong>0 文件</strong></div>
|
||||||
|
<div class="stat-row"><span>Portal 前端</span><strong>0 文件</strong></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文档统计 -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>📚 文档</h2>
|
||||||
|
<div class="big-number">65</div>
|
||||||
|
<div class="label">文档总数</div>
|
||||||
|
<div style="margin-top: 12px;">
|
||||||
|
<div class="stat-row"><span>评审报告</span><strong>6</strong></div><div class="stat-row"><span>审计报告</span><strong>4</strong></div><div class="stat-row"><span>ADRs</span><strong>4</strong></div><div class="stat-row"><span>SOPs</span><strong>4</strong></div><div class="stat-row"><span>路线图</span><strong>3</strong></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 风险状态 -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>🛡️ 风险状态</h2>
|
||||||
|
<div class="big-number" style="color: #dc3545;">-66</div>
|
||||||
|
<div class="label">P0 遗留(需立即修)</div>
|
||||||
|
<div style="margin-top: 12px;">
|
||||||
|
<div class="stat-row"><span>P1 中危</span><span class="badge yellow">-66 待修</span></div>
|
||||||
|
<div class="stat-row"><span>P2 低危</span><span class="badge yellow">-66 待修</span></div>
|
||||||
|
<div class="stat-row"><span>M 中</span><span class="badge blue">-56 待修</span></div>
|
||||||
|
<div class="stat-row"><span>L 低</span><span class="badge blue">-61 待修</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 脚本与测试 -->
|
||||||
|
<div class="card">
|
||||||
|
<h2>🛠️ 工具链</h2>
|
||||||
|
<div class="big-number">8</div>
|
||||||
|
<div class="label">自动化脚本</div>
|
||||||
|
<div style="margin-top: 12px;">
|
||||||
|
<div class="stat-row"><span>后端测试</span><strong>18 文件</strong></div>
|
||||||
|
<div class="stat-row"><span>安全审计</span><span class="badge green">✅ 已配</span></div>
|
||||||
|
<div class="stat-row"><span>API 文档</span><span class="badge green">✅ 已配</span></div>
|
||||||
|
<div class="stat-row"><span>备份脚本</span><span class="badge green">✅ 已配</span></div>
|
||||||
|
<div class="stat-row"><span>Pre-commit</span><span class="badge green">✅ 已配</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Git 状态 -->
|
||||||
|
<div class="card" style="grid-column: span 2;">
|
||||||
|
<h2>📦 Git 状态</h2>
|
||||||
|
<div class="git-info">
|
||||||
|
<div>分支: <span class="hash">feature/t-1-t4-merge</span></div>
|
||||||
|
<div>提交数: <span class="hash">17</span></div>
|
||||||
|
<div>最近提交: <span class="hash">93ba41e feat: 瀹℃壒娴佺▼妯″潡 (T瀹℃壒A瀹℃壒)</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块完成度 -->
|
||||||
|
<div class="card" style="grid-column: span 3;">
|
||||||
|
<h2>✅ 阶段完成度</h2>
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin-top: 12px;">
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div class="big-number" style="font-size: 1.8em; color: #28a745;">66%</div>
|
||||||
|
<div class="label">阶段 1</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div class="big-number" style="font-size: 1.8em; color: #ffc107;">0%</div>
|
||||||
|
<div class="label">阶段 2(转人工)</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div class="big-number" style="font-size: 1.8em; color: #6c757d;">0%</div>
|
||||||
|
<div class="label">阶段 3(H5+WS)</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div class="big-number" style="font-size: 1.8em; color: #6c757d;">规划中</div>
|
||||||
|
<div class="label">阶段 4(AI Wingman)</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div class="big-number" style="font-size: 1.8em; color: #6c757d;">规划中</div>
|
||||||
|
<div class="label">阶段 5(自动化)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="text-align: center; color: rgba(255,255,255,0.7); margin-top: 40px; font-size: 0.9em;">
|
||||||
|
企微 IT 智能服务台 · 健康度仪表盘 v1.0
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 综合 QA 测试报告
|
# 智能IT支持服务台 — 综合 QA 测试报告
|
||||||
|
|
||||||
> 本文档合并历次 QA 测试报告,按时间倒序排列(最新在前)。
|
> 本文档合并历次 QA 测试报告,按时间倒序排列(最新在前)。
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 企微IT智能服务台 — 系统架构、消息收发、知识库迭代说明
|
# 企微智能IT支持服务台 — 系统架构、消息收发、知识库迭代说明
|
||||||
|
|
||||||
> **版本**: v1.1 | **日期**: 2026-06-02 | **负责人**: 宋献(IT支持组组长)
|
> **版本**: v1.1 | **日期**: 2026-06-02 | **负责人**: 宋献(IT支持组组长)
|
||||||
> **目标读者**: 运维团队 / 架构团队 / 开发团队
|
> **目标读者**: 运维团队 / 架构团队 / 开发团队
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
收件人:G端域名审核小组
|
收件人:G端域名审核小组
|
||||||
抄送:周复曙、吕勇、朱付贵
|
抄送:周复曙、吕勇、朱付贵
|
||||||
主题:【域名申请】itsupport.servyou.com.cn — IT智能服务台项目外部子域名申请
|
主题:【域名申请】itsupport.servyou.com.cn — 智能IT支持服务台项目外部子域名申请
|
||||||
|
|
||||||
|
|
||||||
各位领导,好:
|
各位领导,好:
|
||||||
|
|
||||||
IT支持组正在推进"IT智能服务台"项目,借助AI能力提升IT支持的服务质量和效率,现申请外部子域名 itsupport.servyou.com.cn。
|
IT支持组正在推进"智能IT支持服务台"项目,借助AI能力提升IT支持的服务质量和效率,现申请外部子域名 itsupport.servyou.com.cn。
|
||||||
|
|
||||||
项目背景:公司日常IT支持在以下方面仍有提升空间:
|
项目背景:公司日常IT支持在以下方面仍有提升空间:
|
||||||
1. 员工入口体验 — 转人工需另开窗口,AI与人工服务衔接不够流畅,跨企业服务不可达
|
1. 员工入口体验 — 转人工需另开窗口,AI与人工服务衔接不够流畅,跨企业服务不可达
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 - Secret 管理方案
|
# 智能IT支持服务台 - Secret 管理方案
|
||||||
|
|
||||||
**版本**: 1.0
|
**版本**: 1.0
|
||||||
**更新日期**: 2026-06-14
|
**更新日期**: 2026-06-14
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 安全审计报告
|
# 智能IT支持服务台 — 安全审计报告
|
||||||
|
|
||||||
> **编制日期**: 2026-06-14
|
> **编制日期**: 2026-06-14
|
||||||
> **版本**: v1.0
|
> **版本**: v1.0
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
| 项目 | 说明 |
|
| 项目 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 系统名称 | IT智能服务台 |
|
| 系统名称 | 智能IT支持服务台 |
|
||||||
| 部署环境 | 企业内网 (10.90.5.110) |
|
| 部署环境 | 企业内网 (10.90.5.110) |
|
||||||
| 访问方式 | 企微工作台应用 / HTTPS |
|
| 访问方式 | 企微工作台应用 / HTTPS |
|
||||||
| 用户规模 | ~6000人 |
|
| 用户规模 | ~6000人 |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 - 版本更新说明
|
# 智能IT支持服务台 - 版本更新说明
|
||||||
|
|
||||||
**版本**: v1.1.0
|
**版本**: v1.1.0
|
||||||
**更新日期**: 2026-06-14
|
**更新日期**: 2026-06-14
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 项目迁移文档
|
# 智能IT支持服务台 — 项目迁移文档
|
||||||
**生成时间**:2026-06-06
|
**生成时间**:2026-06-06
|
||||||
**来源项目**:`C:\Users\simon\wecom_it_smart_desk`
|
**来源项目**:`C:\Users\simon\wecom_it_smart_desk`
|
||||||
**原型文件**:`C:\Users\simon\WorkBuddy\2026-05-21-16-57-26\agent-workspace-v5_3.html`
|
**原型文件**:`C:\Users\simon\WorkBuddy\2026-05-21-16-57-26\agent-workspace-v5_3.html`
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> 基于火绒终端安全管理系统API说明文档(内网地址: `huorong.oa.servyou-it.com:8080`)
|
> 基于火绒终端安全管理系统API说明文档(内网地址: `huorong.oa.servyou-it.com:8080`)
|
||||||
> 分析日期:2026-06-11
|
> 分析日期:2026-06-11
|
||||||
> 分析人:IT智能服务台项目组
|
> 分析人:智能IT支持服务台项目组
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -557,4 +557,4 @@ IT服务台: employee_id → conversation → 坐席 → 查看安全状态
|
|||||||
|
|
||||||
### 8.3 一句话总结
|
### 8.3 一句话总结
|
||||||
|
|
||||||
> 火绒集成是IT智能服务台从「被动响应」走向「主动安全」的关键一步,建议优先推进P0查询能力,2周内可上线见效。
|
> 火绒集成是智能IT支持服务台从「被动响应」走向「主动安全」的关键一步,建议优先推进P0查询能力,2周内可上线见效。
|
||||||
|
|||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 统一入口(Portal)技术设计文档
|
# 智能IT支持服务台 — 统一入口(Portal)技术设计文档
|
||||||
|
|
||||||
**版本**: v1.1
|
**版本**: v1.1
|
||||||
**日期**: 2026-06-13
|
**日期**: 2026-06-13
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
### 1.1 背景
|
### 1.1 背景
|
||||||
|
|
||||||
当前 IT智能服务台 存在三个独立入口:
|
当前 智能IT支持服务台 存在三个独立入口:
|
||||||
- **用户端** `/itdesk/` — 员工提交工单、查看进度
|
- **用户端** `/itdesk/` — 员工提交工单、查看进度
|
||||||
- **坐席端** `/itagent/` — IT坐席处理会话、AI辅助
|
- **坐席端** `/itagent/` — IT坐席处理会话、AI辅助
|
||||||
- **管理端** `/itadmin/` — 系统配置、数据分析
|
- **管理端** `/itadmin/` — 系统配置、数据分析
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
### 1.2 方案目标
|
### 1.2 方案目标
|
||||||
|
|
||||||
**统一入口架构**:
|
**统一入口架构**:
|
||||||
- 所有用户必须通过 **企微工作台 → IT智能服务台应用** 进入
|
- 所有用户必须通过 **企微工作台 → 智能IT支持服务台应用** 进入
|
||||||
- 进入时自动检测账户关联的角色
|
- 进入时自动检测账户关联的角色
|
||||||
- 提供卡片选择页面,让用户选择进入哪个端
|
- 提供卡片选择页面,让用户选择进入哪个端
|
||||||
- 无坐席/管理角色的用户直接进入用户端
|
- 无坐席/管理角色的用户直接进入用户端
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
│ 用户访问流程 │
|
│ 用户访问流程 │
|
||||||
├─────────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────────┤
|
||||||
│ │
|
│ │
|
||||||
│ 企微工作台 → IT智能服务台应用 │
|
│ 企微工作台 → 智能IT支持服务台应用 │
|
||||||
│ │ │
|
│ │ │
|
||||||
│ ▼ │
|
│ ▼ │
|
||||||
│ ┌─────────────────┐ │
|
│ ┌─────────────────┐ │
|
||||||
@@ -508,7 +508,7 @@ frontend-portal/
|
|||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ IT智能服务台 │
|
│ 智能IT支持服务台 │
|
||||||
│ │
|
│ │
|
||||||
│ 选择您要进入的工作台 │
|
│ 选择您要进入的工作台 │
|
||||||
│ │
|
│ │
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> 基于联软LV7000系列LeagView5版本API接口说明文档(202210SP v1.1)
|
> 基于联软LV7000系列LeagView5版本API接口说明文档(202210SP v1.1)
|
||||||
> 分析日期:2026-06-11
|
> 分析日期:2026-06-11
|
||||||
> 分析人:IT智能服务台项目组
|
> 分析人:智能IT支持服务台项目组
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -675,7 +675,7 @@ aTrust集成为**P1优先级**(联软P0之后),因为:
|
|||||||
|
|
||||||
```
|
```
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ IT智能服务台 │
|
│ 智能IT支持服务台 │
|
||||||
│ (统一集成层) │
|
│ (统一集成层) │
|
||||||
│ │
|
│ │
|
||||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||||
@@ -794,4 +794,4 @@ class UnifiedTerminalInfo:
|
|||||||
|
|
||||||
### 9.3 一句话总结
|
### 9.3 一句话总结
|
||||||
|
|
||||||
> 联软是IT智能服务台打通「员工↔终端」映射的关键系统,与火绒形成「管理+安全」双引擎,加上aTrust补全远程办公,三系统集成将实现终端问题排查的360°全景视角。
|
> 联软是智能IT支持服务台打通「员工↔终端」映射的关键系统,与火绒形成「管理+安全」双引擎,加上aTrust补全远程办公,三系统集成将实现终端问题排查的360°全景视角。
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
| `backend/app/api/agents.py` | 改动 | OTP 双因素(otp-bind/otp-verify/otp-unbind) |
|
| `backend/app/api/agents.py` | 改动 | OTP 双因素(otp-bind/otp-verify/otp-unbind) |
|
||||||
| `frontend-h5/src/api/conversation.ts` | 改动 | mapMessage 字段映射(id→message_id) |
|
| `frontend-h5/src/api/conversation.ts` | 改动 | mapMessage 字段映射(id→message_id) |
|
||||||
| `docker-compose.yml` | 改动 | healthcheck 配置(backend 用 curl 已知坑) |
|
| `docker-compose.yml` | 改动 | healthcheck 配置(backend 用 curl 已知坑) |
|
||||||
| `docs/IT智能服务台-版本更新说明-20250614.md` | 文档 | v1.1.0 发布说明 |
|
| `docs/智能IT支持服务台-版本更新说明-20250614.md` | 文档 | v1.1.0 发布说明 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
|
|
||||||
### 待文档/流程
|
### 待文档/流程
|
||||||
|
|
||||||
- [ ] `docs/IT智能服务台-版本更新说明-20250614.md` 4 处错误修订
|
- [ ] `docs/智能IT支持服务台-版本更新说明-20250614.md` 4 处错误修订
|
||||||
- [ ] workbuddy 推送流程:加 "PR 前 P0 强制评审" 环节
|
- [ ] workbuddy 推送流程:加 "PR 前 P0 强制评审" 环节
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 调试验证指南
|
# 智能IT支持服务台 — 调试验证指南
|
||||||
|
|
||||||
**创建时间**: 2026-06-13
|
**创建时间**: 2026-06-13
|
||||||
**适用环境**: 正式服务器 10.90.5.10 (itsupport.servyou.com.cn)
|
**适用环境**: 正式服务器 10.90.5.10 (itsupport.servyou.com.cn)
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
├─────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────┤
|
||||||
│ │
|
│ │
|
||||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||||
│ │ IT智能服务台(正式) │ │ IT智能服务台-测试 │ │
|
│ │ 智能IT支持服务台(正式) │ │ 智能IT支持服务台-测试 │ │
|
||||||
│ │ │ │ │ │
|
│ │ │ │ │ │
|
||||||
│ │ 可信域名: │ │ 可信域名: │ │
|
│ │ 可信域名: │ │ 可信域名: │ │
|
||||||
│ │ itsupport.xxx │ │ itdesk.amanzac │ │
|
│ │ itsupport.xxx │ │ itdesk.amanzac │ │
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
1. 登录 [企微管理后台](https://work.weixin.qq.com/wework_admin/frame)
|
1. 登录 [企微管理后台](https://work.weixin.qq.com/wework_admin/frame)
|
||||||
2. **应用管理** → **自建** → **创建应用**
|
2. **应用管理** → **自建** → **创建应用**
|
||||||
3. 填写信息:
|
3. 填写信息:
|
||||||
- **应用名称**: `IT智能服务台-测试`
|
- **应用名称**: `智能IT支持服务台-测试`
|
||||||
- **应用logo**: 使用不同颜色(如橙色)区分正式应用
|
- **应用logo**: 使用不同颜色(如橙色)区分正式应用
|
||||||
- **应用介绍**: "仅供IT部门测试使用"
|
- **应用介绍**: "仅供IT部门测试使用"
|
||||||
- **可见范围**: 选择IT部门 + 测试人员
|
- **可见范围**: 选择IT部门 + 测试人员
|
||||||
@@ -209,7 +209,7 @@ VITE_WECOM_CORP_ID=ww_test_xxxxx
|
|||||||
|
|
||||||
| 步骤 | 操作 | 预期结果 |
|
| 步骤 | 操作 | 预期结果 |
|
||||||
|------|------|---------|
|
|------|------|---------|
|
||||||
| 1 | 在企微中找到"IT智能服务台-测试"应用 | 应用显示在工作台 |
|
| 1 | 在企微中找到"智能IT支持服务台-测试"应用 | 应用显示在工作台 |
|
||||||
| 2 | 点击应用 | 跳转到 `https://itdesk.amanzac.com/itdesk/` |
|
| 2 | 点击应用 | 跳转到 `https://itdesk.amanzac.com/itdesk/` |
|
||||||
| 3 | 首次访问 | 跳转企微OAuth2授权页 |
|
| 3 | 首次访问 | 跳转企微OAuth2授权页 |
|
||||||
| 4 | 确认授权 | 跳回H5聊天页面 |
|
| 4 | 确认授权 | 跳回H5聊天页面 |
|
||||||
|
|||||||
+6
-6
@@ -1,8 +1,8 @@
|
|||||||
# IT智能服务台 — 资源申请清单
|
# 智能IT支持服务台 — 资源申请清单
|
||||||
|
|
||||||
> **📌 使用说明(工作流程)**
|
> **📌 使用说明(工作流程)**
|
||||||
>
|
>
|
||||||
> 本文档是 IT智能服务台 所有资源申请需求的**统一汇总入口**,适用于:
|
> 本文档是 智能IT支持服务台 所有资源申请需求的**统一汇总入口**,适用于:
|
||||||
> - 服务器/域名/网络等资源申请
|
> - 服务器/域名/网络等资源申请
|
||||||
> - 外部系统 API 对接申请(联软、火绒、aTrust、北森 eHR 等)
|
> - 外部系统 API 对接申请(联软、火绒、aTrust、北森 eHR 等)
|
||||||
> - 任何需要向其他团队(运维/安全/网络)申请权限或资源的任务
|
> - 任何需要向其他团队(运维/安全/网络)申请权限或资源的任务
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
## 一、背景
|
## 一、背景
|
||||||
|
|
||||||
IT智能服务台(IT Smart Desk)包含两个部署环境,需向运维申请服务器、域名及反向代理资源:
|
智能IT支持服务台(IT Smart Desk)包含两个部署环境,需向运维申请服务器、域名及反向代理资源:
|
||||||
|
|
||||||
| 环境 | 用途 | 部署位置 | 访问方式 |
|
| 环境 | 用途 | 部署位置 | 访问方式 |
|
||||||
|------|------|---------|---------|
|
|------|------|---------|---------|
|
||||||
@@ -87,7 +87,7 @@ IT智能服务台(IT Smart Desk)包含两个部署环境,需向运维申
|
|||||||
#### 建议 Nginx 配置片段
|
#### 建议 Nginx 配置片段
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
# ==================== IT智能服务台 — 预生产 ====================
|
# ==================== 智能IT支持服务台 — 预生产 ====================
|
||||||
# 后端 API
|
# 后端 API
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://10.80.0.129:18080/api/;
|
proxy_pass http://10.80.0.129:18080/api/;
|
||||||
@@ -219,7 +219,7 @@ NAS 环境的 Nginx 已内置于 Docker Compose,**无需运维额外配置**
|
|||||||
|
|
||||||
#### 项目背景
|
#### 项目背景
|
||||||
|
|
||||||
IT智能服务台核心目标之一是**打通员工↔终端的映射链路**。联软LV7000拥有最准确的员工账号→终端设备映射数据(`strusername`字段),是终端信息集成的**核心数据源**。
|
智能IT支持服务台核心目标之一是**打通员工↔终端的映射链路**。联软LV7000拥有最准确的员工账号→终端设备映射数据(`strusername`字段),是终端信息集成的**核心数据源**。
|
||||||
|
|
||||||
#### API 账户(超管自建)
|
#### API 账户(超管自建)
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ IT智能服务台核心目标之一是**打通员工↔终端的映射链路**
|
|||||||
|
|
||||||
- **申请人**:宋献,IT支持组(税友集团),负责终端安全
|
- **申请人**:宋献,IT支持组(税友集团),负责终端安全
|
||||||
- **火绒/联软对接人**:宋献(超管权限,自行创建API账户,受安全团队管理)
|
- **火绒/联软对接人**:宋献(超管权限,自行创建API账户,受安全团队管理)
|
||||||
- **项目**:IT智能服务台(IT Smart Desk)
|
- **项目**:智能IT支持服务台(IT Smart Desk)
|
||||||
- **紧急程度**:预生产反代配置建议 1-2 个工作日内完成;生产环境 NAS 自建,无需运维介入
|
- **紧急程度**:预生产反代配置建议 1-2 个工作日内完成;生产环境 NAS 自建,无需运维介入
|
||||||
- **组织架构说明**:
|
- **组织架构说明**:
|
||||||
- IT支持组 = 终端安全负责团队(非独立"终端安全团队")
|
- IT支持组 = 终端安全负责团队(非独立"终端安全团队")
|
||||||
|
|||||||
+1
-1
@@ -212,7 +212,7 @@ async def send_invite_card(
|
|||||||
"template_card": {
|
"template_card": {
|
||||||
"card_type": "button_interaction",
|
"card_type": "button_interaction",
|
||||||
"source": {
|
"source": {
|
||||||
"desc": "IT智能服务台"
|
"desc": "智能IT支持服务台"
|
||||||
},
|
},
|
||||||
"main_title": {
|
"main_title": {
|
||||||
"title": "🔔 会话邀请"
|
"title": "🔔 会话邀请"
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
# 需求: 应急降级页(H5 + Agent Preview)via 企微"服务窗口"
|
||||||
|
|
||||||
|
**需求提出**: 2026-06-15(经多次澄清,核心场景为 BC/DR)
|
||||||
|
**需求方**: Simon
|
||||||
|
**核心场景**: 🔴 **业务连续性(BC/DR)** — 系统故障时切换至企微原生服务,坐席保留关键功能
|
||||||
|
**入口**: 🏢 **企微"员工服务"应用 → "服务窗口"**(只配 1 个 URL)
|
||||||
|
**关联规则**: [[locked-decisions]] § 应急降级 / [[preview-pages-sync-rule]]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 业务目标(真实场景)
|
||||||
|
|
||||||
|
当本系统出现**特殊情况**(故障 / 不可用 / 合规要求 / 流量过载)时:
|
||||||
|
|
||||||
|
1. **员工侧**: 切换至企微**原生员工服务**(群聊/单聊兜底)
|
||||||
|
2. **坐席侧**: 通过企微"员工服务 → 服务窗口" 链接,使用本系统应急页
|
||||||
|
3. **目标**: 即使主系统挂掉,**核心 IT 服务不中断**
|
||||||
|
|
||||||
|
## 🏢 入口架构(1 URL + 企微 JS-SDK)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ 企微"员工服务"应用(企业已建) │
|
||||||
|
│ └─ "服务窗口" tab(只能配 1 个 URL) │
|
||||||
|
│ └─ https://itsupport.servyou.com.cn/emergency
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ /emergency 页面(身份检测) │
|
||||||
|
│ 1. 加载企微 JS-SDK(不依赖本后端) │
|
||||||
|
│ 2. agentConfig 拿当前 userid │
|
||||||
|
│ 3. 调企微通讯录 API 查 user 详情 │
|
||||||
|
│ 4. 判断身份:是"IT支持-咨询坐席"标签成员 │
|
||||||
|
│ ├─ 是 → router.push('/agent/preview')│
|
||||||
|
│ └─ 否 → router.push('/h5/preview') │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键**:
|
||||||
|
- 身份检测**不依赖本系统后端**(主系统挂时仍可用)
|
||||||
|
- 应急页 2 套(h5 + agent),通过 router.push 切换
|
||||||
|
- 企微通讯录 API 走企微 access_token,跟主系统无关
|
||||||
|
|
||||||
|
## 📋 保留功能(4 件套 + 动态联系人)
|
||||||
|
|
||||||
|
| # | 功能 | 描述 | 数据源 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | 🔍 **快速回复模板** | 100+ 条按关键词搜索 | mock JSON → localStorage |
|
||||||
|
| 2 | 🔍 **排障流程模板** | vpn/邮箱/系统/账号 4 大类 | mock JSON → localStorage |
|
||||||
|
| 3 | 📋 **资源/审批链接** | 12 个常用入口 | mock JSON → localStorage |
|
||||||
|
| 4 | 👥 **应急联系人** | 企微标签"IT支持-咨询坐席"成员 | 企微 API → 单独 localStorage |
|
||||||
|
| **合计** | | | **~750KB + 联系人列表** |
|
||||||
|
|
||||||
|
### 应急联系人(动态,非固定)
|
||||||
|
|
||||||
|
**数据源**: 企微通讯录标签"IT支持-咨询坐席"
|
||||||
|
|
||||||
|
**预同步**:
|
||||||
|
- 主系统正常时,每 30 分钟调企微通讯录 API 查该标签成员
|
||||||
|
- 存到**独立 localStorage key** `emergency_contacts`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"synced_at": "2026-06-15T10:00:00",
|
||||||
|
"tag_id": "TAG_xxx",
|
||||||
|
"members": [
|
||||||
|
{"userid": "zhangsan", "name": "张三", "avatar": "...", "online": true},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**应急展示**:
|
||||||
|
- 列出标签下所有成员
|
||||||
|
- 在线/离线状态(企微 status 接口)
|
||||||
|
- 点击 → 打开企微单聊
|
||||||
|
- 数据 > 1 小时未更新时标红,提示"联系人可能不准,建议手动搜索标签组"
|
||||||
|
|
||||||
|
## 🆘 "特殊情况" 触发与降级
|
||||||
|
|
||||||
|
| 类型 | 触发条件 | 降级动作 |
|
||||||
|
|---|---|---|
|
||||||
|
| 🔴 主系统完全不可用 | API 5xx / 网络断开 | 切企微原生 + 服务窗口 |
|
||||||
|
| 🟡 部分功能故障 | 消息发送失败 / 排队堵死 | 切企微原生 + 工具走应急页 |
|
||||||
|
| 🟠 合规要求 | 必须用企微审计 | 切企微原生 + 应急页工单 |
|
||||||
|
| 🟢 流量过载 | 服务降级中 | 部分功能走应急页 |
|
||||||
|
|
||||||
|
## 📋 范围
|
||||||
|
|
||||||
|
| 项 | H5 应急页 | Agent 应急页 |
|
||||||
|
|---|---|---|
|
||||||
|
| URL | `/h5/preview` | `/agent/preview` |
|
||||||
|
| 统一入口 | `/emergency` | `/emergency` |
|
||||||
|
| 显示组件 | `RightPanel.vue`(3 段式) | `AiAssistantPanel.vue`(4 件套) |
|
||||||
|
| 去除功能 | `ChatPanel`(聊天走企微原生) | `ConversationList` + `ChatArea` + `TopBar` |
|
||||||
|
| 数据源 | **预同步到 localStorage** | **预同步到 localStorage** |
|
||||||
|
| 后端调用 | **无**(主系统可能挂) | **无** |
|
||||||
|
| 用户登录 | **跳过**(应急场景免登) | **跳过**(应急场景免登) |
|
||||||
|
|
||||||
|
## 🔄 数据预同步机制(2 个独立 localStorage)
|
||||||
|
|
||||||
|
### localStorage #1: `emergency_data`(静态工具数据)
|
||||||
|
|
||||||
|
**正常态**:
|
||||||
|
```
|
||||||
|
主后端 /api/emergency-data
|
||||||
|
→ 前端每 30 分钟拉取
|
||||||
|
→ 存到 localStorage("emergency_data")
|
||||||
|
```
|
||||||
|
|
||||||
|
**应急态**:
|
||||||
|
```
|
||||||
|
localStorage("emergency_data") → 应急页直接渲染
|
||||||
|
```
|
||||||
|
|
||||||
|
**内容**: 快速回复 / 排障 / 资源(~750KB)
|
||||||
|
|
||||||
|
### localStorage #2: `emergency_contacts`(动态联系人)
|
||||||
|
|
||||||
|
**正常态**:
|
||||||
|
```
|
||||||
|
企微通讯录 API 查"IT支持-咨询坐席"标签
|
||||||
|
→ 前端每 30 分钟拉取
|
||||||
|
→ 存到 localStorage("emergency_contacts")
|
||||||
|
```
|
||||||
|
|
||||||
|
**应急态**:
|
||||||
|
```
|
||||||
|
localStorage("emergency_contacts") → 应急页渲染联系人列表
|
||||||
|
```
|
||||||
|
|
||||||
|
**内容**: 标签成员列表(动态,可能多人)
|
||||||
|
|
||||||
|
## 🎨 页面要求(应急场景)
|
||||||
|
|
||||||
|
### `/emergency`(身份检测入口,~50 行)
|
||||||
|
|
||||||
|
- 加载企微 JS-SDK(wx.config + wx.agentConfig)
|
||||||
|
- 拿当前 userid
|
||||||
|
- 调企微通讯录 API 查 user 详情 + 标签
|
||||||
|
- 判断是否含"IT支持-咨询坐席"标签
|
||||||
|
- 是坐席 → push /agent/preview,否则 → push /h5/preview
|
||||||
|
- 检测失败 → 显示"请选择身份"2 个大按钮兜底
|
||||||
|
|
||||||
|
### H5 应急页(`/h5/preview`)
|
||||||
|
|
||||||
|
- 顶部: 项目名 + **"🆘 应急模式"** 红色徽章 + 数据更新时间
|
||||||
|
- 主体: `RightPanel` 全宽,3 段式(AI 推送 / 资源 / 趣味问答)
|
||||||
|
- 底部: 固定"主系统异常?此页面帮您继续获得服务"
|
||||||
|
- 移动端: 强制显示(覆盖 `isMobile` 判断)
|
||||||
|
|
||||||
|
### Agent 应急页(`/agent/preview`)
|
||||||
|
|
||||||
|
- 顶栏: 简化版 TopBar + 🆘 徽章
|
||||||
|
- 主体: `AiAssistantPanel` 全宽,4 件套
|
||||||
|
- 联系人: 标签组成员 + 在线状态
|
||||||
|
- 底部: 固定"主系统异常"提示
|
||||||
|
|
||||||
|
## 📁 文件改动清单(调整)
|
||||||
|
|
||||||
|
### H5 端 (frontend-h5/)
|
||||||
|
|
||||||
|
| 操作 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| 新建 | `src/views/EmergencyEntry.vue` | 身份检测入口(~50 行) |
|
||||||
|
| 新建 | `src/views/H5PreviewView.vue` | 应急主页 |
|
||||||
|
| 新建 | `src/mock/emergency-data.json` | 应急静态数据 |
|
||||||
|
| 新建 | `src/utils/emergency-sync.ts` | 同步工具(2 个 localStorage) |
|
||||||
|
| 改 | `src/router/index.ts` | 加 `/emergency` + `/h5/preview` |
|
||||||
|
| **复用** | `src/components/assistant/RightPanel.vue` | import 共享 |
|
||||||
|
|
||||||
|
### Agent 端 (frontend-agent/)
|
||||||
|
|
||||||
|
| 操作 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| 新建 | `src/views/AgentPreviewView.vue` | 应急主页 |
|
||||||
|
| 改 | `src/router/index.ts` | 加 `/agent/preview` |
|
||||||
|
| **复用** | `src/components/assistant/AiAssistantPanel.vue` | import 共享 |
|
||||||
|
|
||||||
|
**注**: 联系人 API 调用放 `emergency-sync.ts` 共用模块,h5 + agent 都用
|
||||||
|
|
||||||
|
## 🧪 验收标准
|
||||||
|
|
||||||
|
### 功能验收
|
||||||
|
|
||||||
|
- [ ] **断网测试**: 拔网线/关后端 → 应急页仍能打开
|
||||||
|
- [ ] **身份自动路由**: 员工点开 → /h5/preview,坐席点开 → /agent/preview
|
||||||
|
- [ ] **联系人动态**: 标签组加新人,预同步后能显示
|
||||||
|
- [ ] **数据新鲜度**: 顶部"数据 X 分钟前更新",> 1h 标红
|
||||||
|
- [ ] **2 个 localStorage 独立工作**: 删 emergency_data 不影响 emergency_contacts
|
||||||
|
|
||||||
|
### 灾备演练(非工作时间,本月必做)
|
||||||
|
|
||||||
|
- [ ] 选择非工作时间(晚上 / 周末)
|
||||||
|
- [ ] 模拟主系统挂掉 → 切企微原生服务 → 坐席用应急页
|
||||||
|
- [ ] 演练时长 / 解决率 / 痛点记录
|
||||||
|
- [ ] 详见 `docs/SOPs/SOP-005-应急降级演练.md`
|
||||||
|
|
||||||
|
## 📅 排期
|
||||||
|
|
||||||
|
| 时间 | 任务 |
|
||||||
|
|---|---|
|
||||||
|
| 2026-06-16 (周一) | WB 接单,1 入口 + 2 页面 + 1 mock + 1 同步工具 + 2 路由 |
|
||||||
|
| 2026-06-16 (周一) | 本地 `npm run build` 验证 |
|
||||||
|
| 2026-06-17 (周二) | 部署 + **断网演练**(非工作时间,如周二晚 20:00) |
|
||||||
|
| 2026-06-18 (周三) | 收集问题,迭代 |
|
||||||
|
| 2026-06-19 (周四) | 二次演练确认 |
|
||||||
|
| 2026-06-20 (周五) | 正式版上线 + 应急页同步上线 |
|
||||||
|
|
||||||
|
## ⚠️ 应急场景的额外考虑
|
||||||
|
|
||||||
|
1. **入口要醒目**: 企微"员工服务 → 服务窗口"配置清晰描述
|
||||||
|
2. **不依赖登录**: 应急时坐席可能密码都改不了,免登
|
||||||
|
3. **非工作时间演练**: 降低对业务影响
|
||||||
|
4. **联系人降级**: 实在没预同步数据,提示"请手动搜索 IT支持-咨询坐席 标签"
|
||||||
|
5. **保留升级路径**: 主系统恢复后,应急页要有提示"主系统已恢复,建议返回"
|
||||||
|
|
||||||
|
## 🔗 关联
|
||||||
|
|
||||||
|
- **双端同步规则**: [[preview-pages-sync-rule]]
|
||||||
|
- **锁定决策**: [[locked-decisions]] § 应急降级
|
||||||
|
- **演练 SOP**: `docs/SOPs/SOP-005-应急降级演练.md`
|
||||||
|
- **需求演进**: v1 灰度(误)→ v2 BC/DR(理解)→ v3 服务窗口(1 URL)→ v4 标签联系人(动态)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 项目任务状态报告
|
# 智能IT支持服务台 — 项目任务状态报告
|
||||||
|
|
||||||
**报告时间**: 2026-06-13 11:00
|
**报告时间**: 2026-06-13 11:00
|
||||||
**报告版本**: v1.0
|
**报告版本**: v1.0
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
2. **构建并部署最新代码**:将今天的 Bug 修复 + UI 风格更新部署到服务器
|
2. **构建并部署最新代码**:将今天的 Bug 修复 + UI 风格更新部署到服务器
|
||||||
|
|
||||||
### 近期安排(P1)
|
### 近期安排(P1)
|
||||||
3. **创建测试企微应用**:按照双企微应用方案,创建"IT智能服务台-测试"应用
|
3. **创建测试企微应用**:按照双企微应用方案,创建"智能IT支持服务台-测试"应用
|
||||||
4. **阶段二启动**:排队机制 + 满意度评价设计
|
4. **阶段二启动**:排队机制 + 满意度评价设计
|
||||||
5. **aTrust对接**:找信息安全团队获取API密钥
|
5. **aTrust对接**:找信息安全团队获取API密钥
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 项目开发任务调整建议
|
# 智能IT支持服务台 — 项目开发任务调整建议
|
||||||
|
|
||||||
> **文档版本**: V1.0
|
> **文档版本**: V1.0
|
||||||
> **创建日期**: 2026-06-11
|
> **创建日期**: 2026-06-11
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
# IT智能服务台 — 风险跟踪表
|
# 智能IT支持服务台 — 风险跟踪表
|
||||||
|
|
||||||
**最后更新**: 2026-06-14 18:30
|
**最后更新**: 2026-06-14 18:30
|
||||||
**维护人**: 宋献 + Claude 评审协作
|
**维护人**: 宋献 + Claude 评审协作
|
||||||
@@ -567,7 +567,7 @@ location /api/ {
|
|||||||
## 九、2026-06-14 workbuddy 推送评审新增
|
## 九、2026-06-14 workbuddy 推送评审新增
|
||||||
|
|
||||||
**评审依据**: `docs/评审报告/workbuddy-2026-06-14-消息优化.md`
|
**评审依据**: `docs/评审报告/workbuddy-2026-06-14-消息优化.md`
|
||||||
**评审范围**: workbuddy 6-14 推送 + `IT智能服务台-版本更新说明-20250614.md`
|
**评审范围**: workbuddy 6-14 推送 + `智能IT支持服务台-版本更新说明-20250614.md`
|
||||||
**小计**: 13 项发现(6 P0 + 4 P1 + 3 P2),其中 7 项已修本地代码,6 项待 workbuddy 跟进
|
**小计**: 13 项发现(6 P0 + 4 P1 + 3 P2),其中 7 项已修本地代码,6 项待 workbuddy 跟进
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -650,7 +650,7 @@ location /api/ {
|
|||||||
|
|
||||||
- **状态**: ⚠️ 待处理
|
- **状态**: ⚠️ 待处理
|
||||||
- **风险级别**: 🟠 高(文档与代码不符)
|
- **风险级别**: 🟠 高(文档与代码不符)
|
||||||
- **位置**: `docs/IT智能服务台-版本更新说明-20250614.md:46` 声称改动 / `backend/app/services/ws_manager.py` 实际无对应方法
|
- **位置**: `docs/智能IT支持服务台-版本更新说明-20250614.md:46` 声称改动 / `backend/app/services/ws_manager.py` 实际无对应方法
|
||||||
- **问题**: ConnectionManager 仅有 `send_to_agent` / `broadcast` / `send_to_employee` / `broadcast_to_employees`,**无 `broadcast_message_status(conv_id, msg_id, status)`**
|
- **问题**: ConnectionManager 仅有 `send_to_agent` / `broadcast` / `send_to_employee` / `broadcast_to_employees`,**无 `broadcast_message_status(conv_id, msg_id, status)`**
|
||||||
- **处理建议**: 实现该方法 + WebSocket 消息格式
|
- **处理建议**: 实现该方法 + WebSocket 消息格式
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- CSP 安全策略 -->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https: http:; connect-src 'self' https://qyapi.weixin.qq.com wss://*;" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>IT智能服务台 - 管理后台</title>
|
<title>智能IT支持服务台 - 管理后台</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "企微IT智能服务台 - 管理后台前端",
|
"description": "企微智能IT支持服务台 - 管理后台前端",
|
||||||
|
"engines": { "node": ">=20.0.0 <21.0.0", "pnpm": ">=9.0.0" },
|
||||||
|
"packageManager": "pnpm@9.15.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc && vite build",
|
"build": "vue-tsc && vite build",
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<!-- 移动端视口设置 -->
|
<!-- 移动端视口设置 -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- CSP 安全策略 -->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https: http:; connect-src 'self' https://qyapi.weixin.qq.com wss://*;" />
|
||||||
<!-- 页面标题 -->
|
<!-- 页面标题 -->
|
||||||
<title>IT智能服务台 - 坐席工作台</title>
|
<title>智能IT支持服务台 - 坐席工作台</title>
|
||||||
<!-- ElementPlus 图标 -->
|
<!-- ElementPlus 图标 -->
|
||||||
<link rel="icon" type="image/svg+xml" href="/itagent/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/itagent/vite.svg" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"name": "wecom-it-desk-agent",
|
"name": "wecom-it-desk-agent",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "企微IT智能服务台 - 坐席工作台前端",
|
"description": "企微智能IT支持服务台 - 坐席工作台前端",
|
||||||
|
"engines": { "node": ">=20.0.0 <21.0.0", "pnpm": ">=9.0.0" },
|
||||||
|
"packageManager": "pnpm@9.15.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc && vite build",
|
"build": "vue-tsc && vite build",
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<!-- 移动端视口设置(适配企微 WebView) -->
|
<!-- 移动端视口设置(适配企微 WebView) -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<!-- CSP 安全策略 -->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https: http:; connect-src 'self' https://qyapi.weixin.qq.com wss://*;" />
|
||||||
<!-- 页面标题 -->
|
<!-- 页面标题 -->
|
||||||
<title>IT智能服务台</title>
|
<title>智能IT支持服务台</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Vue 应用挂载点 -->
|
<!-- Vue 应用挂载点 -->
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"name": "wecom-it-desk-h5",
|
"name": "wecom-it-desk-h5",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "企微IT智能服务台 - H5用户端前端",
|
"description": "企微智能IT支持服务台 - H5用户端前端",
|
||||||
|
"engines": { "node": ">=20.0.0 <21.0.0", "pnpm": ">=9.0.0" },
|
||||||
|
"packageManager": "pnpm@9.15.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc && vite build",
|
"build": "vue-tsc && vite build",
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>IT智能服务台 - 选择工作台</title>
|
<!-- CSP 安全策略 -->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https: http:; connect-src 'self' https://qyapi.weixin.qq.com wss://*;" />
|
||||||
|
<title>智能IT支持服务台 - 选择工作台</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"engines": { "node": ">=20.0.0 <21.0.0", "pnpm": ">=9.0.0" },
|
||||||
|
"packageManager": "pnpm@9.15.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc && vite build",
|
"build": "vue-tsc && vite build",
|
||||||
|
|||||||
+14
@@ -47,6 +47,20 @@ http {
|
|||||||
application/javascript application/xml+rss
|
application/javascript application/xml+rss
|
||||||
application/json application/ld+json;
|
application/json application/ld+json;
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 安全响应头
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 隐藏 nginx 版本号
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# 基础安全头(应用到所有响应)
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header X-XSS-Protection "0" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# 上游服务定义(Docker 内部网络)
|
# 上游服务定义(Docker 内部网络)
|
||||||
# =================================================================
|
# =================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user