Browse Source

chore: 新增部署脚本、API 文档及测试数据

何文松 1 day ago
parent
commit
5648057071
4 changed files with 1285 additions and 0 deletions
  1. 324 0
      deploy-sygpudev.sh
  2. 961 0
      docs/API接口文档.md
  3. BIN
      mock_docs/.~模板.docx
  4. 0 0
      mock_docs/parsed_doc_full.json

+ 324 - 0
deploy-sygpudev.sh

@@ -0,0 +1,324 @@
+#!/bin/bash
+
+# ============================================
+# 灵越智报 - 发布到 sygpudev 服务器
+# 本地编译 → 上传 jar → 重启服务
+# ============================================
+
+# 不使用 set -e,因为 ssh 远程命令的退出码会导致误退出
+
+# 颜色定义
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m'
+
+# ========== 配置区域 ==========
+SERVER_USER="root"
+SERVER_HOST="sygpudev"
+SERVER_PORT="${SERVER_PORT:-28529}"
+
+# 路径配置
+LOCAL_PROJECT_DIR="/home/hws/workspace/GitLab/ay/lingyue-zhibao"
+LOCAL_BACKEND_DIR="${LOCAL_PROJECT_DIR}/backend"
+LOCAL_JAR_PATH="${LOCAL_BACKEND_DIR}/lingyue-starter/target/lingyue-starter.jar"
+
+REMOTE_APP_DIR="/data/application/lingyue-zhibao"
+REMOTE_JAR="${REMOTE_APP_DIR}/lingyue-starter.jar"
+REMOTE_LOG_DIR="${REMOTE_APP_DIR}/logs"
+
+# 应用配置
+JAR_NAME="lingyue-starter.jar"
+# ==============================
+
+log_info()  { echo -e "${GREEN}[INFO]${NC} $1"; }
+log_warn()  { echo -e "${YELLOW}[WARN]${NC} $1"; }
+log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
+log_title() { echo -e "\n${BLUE}========== $1 ==========${NC}\n"; }
+
+ssh_cmd() {
+    ssh -p ${SERVER_PORT} ${SERVER_USER}@${SERVER_HOST} "$@"
+}
+
+# 检查服务器连接
+check_connection() {
+    log_title "检查服务器连接"
+
+    if ssh_cmd "echo 'OK'" > /dev/null 2>&1; then
+        log_info "服务器连接成功: ${SERVER_USER}@${SERVER_HOST}"
+    else
+        log_error "无法连接服务器: ${SERVER_USER}@${SERVER_HOST}"
+        log_warn "请检查:"
+        log_warn "  1. 服务器地址是否正确(可在 /etc/hosts 或 ~/.ssh/config 中配置 sygpudev)"
+        log_warn "  2. SSH 密钥是否配置"
+        log_warn "  3. 可以通过 SERVER_HOST=<IP> 指定服务器地址"
+        exit 1
+    fi
+}
+
+# 本地编译
+build_local() {
+    log_title "本地编译项目"
+
+    cd ${LOCAL_BACKEND_DIR}
+    log_info "清理并编译..."
+    mvn clean package -DskipTests -q
+
+    if [ ! -f "${LOCAL_JAR_PATH}" ]; then
+        log_error "编译产物不存在: ${LOCAL_JAR_PATH}"
+        exit 1
+    fi
+
+    local jar_size=$(du -h "${LOCAL_JAR_PATH}" | awk '{print $1}')
+    log_info "编译完成,jar 大小: ${jar_size}"
+}
+
+# 停止远程应用
+stop_app() {
+    log_title "停止远程应用"
+
+    # 优先使用服务器上的 start.sh stop(如果支持)
+    ssh_cmd "test -f ${REMOTE_APP_DIR}/start.sh && cd ${REMOTE_APP_DIR} && bash start.sh stop; true"
+    sleep 2
+
+    # 确保进程已停止
+    ssh_cmd "pkill -f '${JAR_NAME}'; true"
+    sleep 2
+
+    if ssh_cmd "pgrep -f '${JAR_NAME}'" > /dev/null 2>&1; then
+        log_warn "进程仍在运行,强制终止..."
+        ssh_cmd "pkill -9 -f '${JAR_NAME}'; true"
+        sleep 2
+    fi
+
+    log_info "应用已停止"
+}
+
+# 上传 jar
+upload_jar() {
+    log_title "上传 JAR 到服务器"
+
+    # 备份旧 jar
+    ssh_cmd "cd ${REMOTE_APP_DIR} && \
+             [ -f ${JAR_NAME} ] && cp ${JAR_NAME} ${JAR_NAME}.bak || true"
+    log_info "已备份旧版本为 ${JAR_NAME}.bak"
+
+    # 上传新 jar
+    log_info "正在上传 ${JAR_NAME}..."
+    scp -P ${SERVER_PORT} "${LOCAL_JAR_PATH}" "${SERVER_USER}@${SERVER_HOST}:${REMOTE_JAR}"
+
+    local remote_size=$(ssh_cmd "du -h ${REMOTE_JAR}" | awk '{print $1}')
+    log_info "上传完成,远程文件大小: ${remote_size}"
+}
+
+# 启动远程应用
+start_app() {
+    log_title "启动远程应用"
+
+    # 优先使用服务器上的 start.sh
+    if ssh_cmd "test -f ${REMOTE_APP_DIR}/start.sh" 2>/dev/null; then
+        log_info "使用服务器 start.sh 启动..."
+        ssh_cmd "cd ${REMOTE_APP_DIR} && bash start.sh start"
+    else
+        log_info "直接启动 jar..."
+        ssh_cmd "cd ${REMOTE_APP_DIR} && \
+                 mkdir -p ${REMOTE_LOG_DIR} && \
+                 nohup java -Xms1g -Xmx2g -XX:+UseG1GC \
+                       -jar ${JAR_NAME} \
+                       --spring.config.location=file:./application.yml \
+                       > ${REMOTE_LOG_DIR}/app.log 2>&1 &"
+    fi
+
+    log_info "应用启动中..."
+    sleep 5
+
+    if ssh_cmd "pgrep -f '${JAR_NAME}'" > /dev/null 2>&1; then
+        local pid=$(ssh_cmd "pgrep -f '${JAR_NAME}'")
+        log_info "应用启动成功 (PID: ${pid})"
+    else
+        log_error "应用启动失败,查看日志:"
+        ssh_cmd "tail -50 ${REMOTE_LOG_DIR}/app.log 2>/dev/null || tail -50 /var/log/lingyue.log 2>/dev/null || echo '未找到日志文件'"
+        exit 1
+    fi
+}
+
+# 健康检查
+health_check() {
+    log_title "健康检查"
+
+    local max_attempts=12
+    local attempt=1
+
+    while [ $attempt -le $max_attempts ]; do
+        log_info "尝试 ${attempt}/${max_attempts}..."
+
+        local response=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://localhost:8001/api/v1/projects 2>/dev/null" || echo "000")
+
+        if [ "$response" != "000" ] && [ "$response" != "404" ]; then
+            log_info "健康检查通过 ✓ (HTTP ${response})"
+            echo ""
+            return 0
+        fi
+
+        sleep 5
+        attempt=$((attempt + 1))
+    done
+
+    log_warn "健康检查超时,应用可能仍在启动中"
+    log_info "可手动检查: ssh ${SERVER_USER}@${SERVER_HOST} 'curl http://localhost:8001/actuator/health'"
+}
+
+# 回滚到上一版本
+rollback() {
+    log_title "回滚到上一版本"
+
+    if ssh_cmd "test -f ${REMOTE_APP_DIR}/${JAR_NAME}.bak" 2>/dev/null; then
+        stop_app
+        ssh_cmd "cd ${REMOTE_APP_DIR} && mv ${JAR_NAME}.bak ${JAR_NAME}"
+        log_info "已恢复上一版本"
+        start_app
+        health_check
+    else
+        log_error "没有找到备份文件 ${JAR_NAME}.bak,无法回滚"
+        exit 1
+    fi
+}
+
+# 查看远程日志
+show_logs() {
+    log_title "应用日志"
+    ssh_cmd "tail -100 ${REMOTE_LOG_DIR}/app.log 2>/dev/null || tail -100 /var/log/lingyue.log 2>/dev/null || echo '未找到日志文件'"
+}
+
+tail_logs() {
+    log_info "实时日志 (Ctrl+C 退出)"
+    ssh_cmd "tail -f ${REMOTE_LOG_DIR}/app.log 2>/dev/null || tail -f /var/log/lingyue.log 2>/dev/null || echo '未找到日志文件'"
+}
+
+show_status() {
+    log_title "应用状态"
+
+    if ssh_cmd "pgrep -f '${JAR_NAME}'" > /dev/null 2>&1; then
+        local pid=$(ssh_cmd "pgrep -f '${JAR_NAME}'")
+        log_info "应用运行中 (PID: ${pid})"
+        ssh_cmd "ps -p ${pid} -o pid,rss,vsz,etime,args --no-headers" 2>/dev/null || true
+    else
+        log_warn "应用未运行"
+    fi
+}
+
+# 完整部署: 编译 → 上传 → 重启
+full_deploy() {
+    check_connection
+    build_local
+    stop_app
+    upload_jar
+    start_app
+    health_check
+
+    log_title "部署完成 🎉"
+    echo -e "${GREEN}服务器: ${SERVER_USER}@${SERVER_HOST}:${REMOTE_APP_DIR}${NC}"
+    echo ""
+}
+
+# 快速部署: 仅上传 jar 并重启(跳过编译,使用已有的 target)
+quick_deploy() {
+    if [ ! -f "${LOCAL_JAR_PATH}" ]; then
+        log_error "本地 jar 不存在,请先编译或使用 deploy 命令"
+        exit 1
+    fi
+
+    check_connection
+    stop_app
+    upload_jar
+    start_app
+    health_check
+
+    log_title "快速部署完成 🎉"
+}
+
+# 帮助
+show_help() {
+    cat <<EOF
+灵越智报 - 发布到 sygpudev 服务器
+
+用法: ./deploy-sygpudev.sh [命令]
+
+命令:
+    deploy          完整部署(本地编译 + 上传 + 重启)
+    quick           快速部署(上传已编译的 jar + 重启)
+    build           仅本地编译
+    upload          仅上传 jar(不重启)
+    restart         重启远程应用(不编译不上传)
+    stop            停止远程应用
+    rollback        回滚到上一版本
+    status          查看应用状态
+    logs            查看最近日志
+    tail            实时查看日志
+    help            显示此帮助
+
+示例:
+    ./deploy-sygpudev.sh deploy       # 完整部署
+    ./deploy-sygpudev.sh quick        # 快速部署(已编译过)
+    ./deploy-sygpudev.sh rollback     # 回滚到上一版本
+    ./deploy-sygpudev.sh tail         # 实时看日志
+
+EOF
+}
+
+# 主函数
+main() {
+    case "${1:-deploy}" in
+        deploy)
+            full_deploy
+            ;;
+        quick)
+            quick_deploy
+            ;;
+        build)
+            build_local
+            ;;
+        upload)
+            check_connection
+            upload_jar
+            log_info "上传完成(未重启)"
+            ;;
+        restart)
+            check_connection
+            stop_app
+            start_app
+            health_check
+            ;;
+        stop)
+            check_connection
+            stop_app
+            ;;
+        rollback)
+            check_connection
+            rollback
+            ;;
+        status)
+            check_connection
+            show_status
+            ;;
+        logs)
+            check_connection
+            show_logs
+            ;;
+        tail)
+            check_connection
+            tail_logs
+            ;;
+        help|--help|-h)
+            show_help
+            ;;
+        *)
+            log_error "未知命令: $1"
+            show_help
+            exit 1
+            ;;
+    esac
+}
+
+main "$@"

+ 961 - 0
docs/API接口文档.md

@@ -0,0 +1,961 @@
+# 灵越智报 - 前端接口接入文档
+
+> **Base URL**: `http://47.108.80.98:8001`  
+> **版本**: v1  
+> **更新日期**: 2026-02-13
+
+---
+
+## 通用说明
+
+### 响应格式
+
+所有接口统一返回格式:
+
+```json
+{
+  "code": 200,
+  "message": "success",
+  "data": { ... }
+}
+```
+
+| code | 说明 |
+|------|------|
+| 200  | 成功 |
+| 400  | 参数错误 |
+| 401  | 未认证 / Token 过期 |
+| 403  | 无权限 |
+| 404  | 资源不存在 |
+| 500  | 服务器内部错误 |
+
+### 认证方式
+
+除登录接口外,所有接口需在请求头中携带 Token:
+
+```
+Authorization: Bearer <accessToken>
+```
+
+### 分页参数(适用于列表接口)
+
+| 参数 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| page | int  | 1      | 页码 |
+| size | int  | 20     | 每页条数 |
+
+### 分页响应结构
+
+```json
+{
+  "code": 200,
+  "data": {
+    "records": [ ... ],
+    "total": 100,
+    "page": 1,
+    "size": 20,
+    "pages": 5
+  }
+}
+```
+
+---
+
+## 1. 认证模块
+
+### 1.1 用户登录
+
+```
+POST /api/v1/auth/login
+```
+
+**请求体**:
+
+```json
+{
+  "username": "admin",
+  "password": "admin123"
+}
+```
+
+**响应**:
+
+```json
+{
+  "code": 200,
+  "data": {
+    "accessToken": "eyJhbGciOiJIUzM4NCJ9...",
+    "refreshToken": "xxx",
+    "expiresIn": 86400,
+    "user": {
+      "id": 1,
+      "username": "admin",
+      "realName": "管理员",
+      "avatar": null,
+      "roles": ["admin"]
+    }
+  }
+}
+```
+
+### 1.2 用户登出
+
+```
+POST /api/v1/auth/logout
+```
+
+**请求头**:`Authorization: Bearer <accessToken>`
+
+### 1.3 刷新 Token
+
+```
+POST /api/v1/auth/refresh
+```
+
+**请求体**:
+
+```json
+{
+  "refreshToken": "xxx"
+}
+```
+
+**响应**:同登录响应。
+
+### 1.4 获取当前用户信息
+
+```
+GET /api/v1/auth/me
+```
+
+**响应**:
+
+```json
+{
+  "code": 200,
+  "data": {
+    "id": 1,
+    "username": "admin",
+    "realName": "管理员",
+    "avatar": null,
+    "roles": ["admin"]
+  }
+}
+```
+
+### 1.5 修改密码
+
+```
+PUT /api/v1/auth/password
+```
+
+**请求体**:
+
+```json
+{
+  "oldPassword": "admin123",
+  "newPassword": "newpass456"
+}
+```
+
+### 1.6 获取当前用户权限树
+
+```
+GET /api/v1/auth/permissions
+```
+
+**响应**:
+
+```json
+{
+  "code": 200,
+  "data": [
+    {
+      "id": 1,
+      "permKey": "project:view",
+      "permName": "查看项目",
+      "children": [ ... ]
+    }
+  ]
+}
+```
+
+---
+
+## 2. 项目模块
+
+### 2.1 项目列表(分页)
+
+```
+GET /api/v1/projects
+```
+
+**查询参数**:
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| page | int | 否 | 页码,默认 1 |
+| size | int | 否 | 每页条数,默认 20 |
+| status | string | 否 | 状态筛选 |
+| templateType | string | 否 | 模板类型筛选 |
+| keyword | string | 否 | 关键词搜索 |
+
+**响应 `data`**:
+
+```json
+{
+  "records": [
+    {
+      "id": 10,
+      "title": "成都院安全生产标准化复审",
+      "projectCode": "PRJ-2024-001",
+      "status": "active",
+      "description": "",
+      "templateType": "safety_review",
+      "elementCount": 47,
+      "attachmentCount": 9,
+      "ruleCount": 0,
+      "filledCount": 47,
+      "createdByName": "管理员",
+      "createdAt": "2026-02-13T00:38:50",
+      "updatedAt": "2026-02-13T00:38:50"
+    }
+  ],
+  "total": 1,
+  "page": 1,
+  "size": 20,
+  "pages": 1
+}
+```
+
+### 2.2 项目详情
+
+```
+GET /api/v1/projects/{id}
+```
+
+**响应 `data`**:
+
+```json
+{
+  "project": {
+    "id": 10,
+    "title": "成都院安全生产标准化复审",
+    "projectCode": "PRJ-2024-001",
+    "status": "active",
+    "description": "",
+    "templateType": "safety_review",
+    "elementCount": 47,
+    "attachmentCount": 9,
+    "ruleCount": 0,
+    "filledCount": 47,
+    "contentHtml": null,
+    "contentMarkdown": null,
+    "createdByName": "管理员",
+    "createdAt": "2026-02-13T00:38:50",
+    "updatedAt": "2026-02-13T00:38:50"
+  },
+  "elements": [ ... ],
+  "values": [ ... ]
+}
+```
+
+### 2.3 创建项目
+
+```
+POST /api/v1/projects
+```
+
+**请求体**:
+
+```json
+{
+  "title": "新项目名称",
+  "description": "项目描述",
+  "templateType": "safety_review",
+  "fromProjectId": null
+}
+```
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| title | string | ✅ | 项目标题 |
+| description | string | 否 | 项目描述 |
+| templateType | string | 否 | 模板类型 |
+| fromProjectId | long | 否 | 从已有项目复制 |
+
+**响应 `data`**:`ProjectVO` 对象。
+
+### 2.4 更新项目
+
+```
+PUT /api/v1/projects/{id}
+```
+
+**请求体**:
+
+```json
+{
+  "title": "更新后的标题",
+  "description": "更新后的描述",
+  "templateType": "safety_review"
+}
+```
+
+### 2.5 删除项目
+
+```
+DELETE /api/v1/projects/{id}
+```
+
+### 2.6 复制项目
+
+```
+POST /api/v1/projects/{id}/copy
+```
+
+**响应 `data`**:新创建的 `ProjectVO` 对象。
+
+### 2.7 归档项目
+
+```
+PUT /api/v1/projects/{id}/archive
+```
+
+### 2.8 导出项目
+
+```
+POST /api/v1/projects/{id}/export?format=docx
+```
+
+**响应**:二进制文件流(`Content-Type: application/octet-stream`)。
+
+### 2.9 获取项目文档内容
+
+```
+GET /api/v1/projects/{id}/doc-content
+```
+
+返回模板文档的结构化 JSON,用于前端渲染文档视图和要素高亮。
+
+**响应 `data`**:
+
+```json
+{
+  "page": {
+    "widthMm": 210.0,
+    "heightMm": 297.0,
+    "marginTopMm": 25.4,
+    "marginBottomMm": 20.0,
+    "marginLeftMm": 28.0,
+    "marginRightMm": 28.0
+  },
+  "blocks": [
+    {
+      "id": "b0",
+      "type": "paragraph",
+      "runs": [
+        {
+          "text": "正文内容",
+          "bold": true,
+          "fontSize": 14.0,
+          "fontFamily": "宋体"
+        }
+      ],
+      "style": {
+        "alignment": "center",
+        "lineSpacing": 1.5
+      },
+      "images": [
+        {
+          "rId": "rId8",
+          "src": "data:image/png;base64,iVBOR...",
+          "widthInch": 6.3,
+          "heightInch": 1.2
+        }
+      ]
+    },
+    {
+      "id": "b5",
+      "type": "table",
+      "table": {
+        "rows": 10,
+        "cols": 4,
+        "data": [
+          [
+            { "text": "序号", "colspan": 1 },
+            { "text": "项目名称", "colspan": 1 }
+          ]
+        ]
+      }
+    }
+  ],
+  "totalBlocks": 311
+}
+```
+
+**Block 类型说明**:
+
+| type | 说明 |
+|------|------|
+| `paragraph` | 普通段落 |
+| `heading1` ~ `heading3` | 标题 |
+| `table` | 表格 |
+| `toc1` ~ `toc3` | 目录项 |
+| `list_item` | 列表项 |
+
+**Run 格式字段**:
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| text | string | 文本内容 |
+| bold | boolean | 粗体 |
+| italic | boolean | 斜体 |
+| underline | string | 下划线类型 |
+| fontSize | number | 字号(pt) |
+| fontFamily | string | 字体名称 |
+| color | string | 文字颜色(RGB) |
+
+---
+
+## 3. 要素模块
+
+### 3.1 获取项目要素列表
+
+```
+GET /api/v1/projects/{projectId}/elements
+```
+
+**响应 `data`**:
+
+```json
+[
+  {
+    "id": 701,
+    "elementKey": "basicInfo.projectCode",
+    "elementName": "项目编号",
+    "elementType": "text",
+    "namespace": "basicInfo",
+    "fieldName": "projectCode",
+    "tableColumns": null,
+    "required": true,
+    "defaultValue": null,
+    "description": "安全生产标准化项目编号",
+    "projectId": 10,
+    "sortOrder": 0,
+    "createdAt": "2026-02-13T00:38:50"
+  }
+]
+```
+
+**elementType 取值**:
+
+| 值 | 说明 | 高亮方式 |
+|------|------|------|
+| `text` | 短文本 | 行内子串匹配,背景色高亮 |
+| `paragraph` | 长文本/段落 | 整段匹配,左边框 + 背景色 |
+| `table` | 表格 | 表格外框高亮 |
+
+### 3.2 添加要素定义
+
+```
+POST /api/v1/projects/{projectId}/elements
+```
+
+**请求体**:
+
+```json
+{
+  "elementKey": "basicInfo.newField",
+  "elementName": "新字段",
+  "elementType": "text",
+  "namespace": "basicInfo",
+  "fieldName": "newField",
+  "required": false,
+  "defaultValue": "",
+  "description": "字段描述",
+  "sortOrder": 50
+}
+```
+
+### 3.3 更新要素定义
+
+```
+PUT /api/v1/projects/{projectId}/elements/{elementId}
+```
+
+**请求体**:同添加。
+
+### 3.4 删除要素定义
+
+```
+DELETE /api/v1/projects/{projectId}/elements/{elementId}
+```
+
+---
+
+## 4. 要素值模块
+
+### 4.1 获取所有要素值
+
+```
+GET /api/v1/projects/{projectId}/values
+```
+
+**响应 `data`**:
+
+```json
+[
+  {
+    "valueId": 801,
+    "elementKey": "PRJ-2024-001:basicInfo.projectCode",
+    "valueText": "BZ-0092-2024",
+    "valueJson": null,
+    "isFilled": true,
+    "fillSource": "manual",
+    "projectId": 10,
+    "createdAt": "2026-02-13T00:38:50",
+    "updatedAt": "2026-02-13T00:38:50"
+  }
+]
+```
+
+> **注意**:`elementKey` 带有项目前缀(如 `PRJ-2024-001:`),前端匹配要素时需去除前缀。
+
+### 4.2 获取单个要素值
+
+```
+GET /api/v1/projects/{projectId}/values/{elementKey}
+```
+
+> `elementKey` 需要 URL 编码(如 `basicInfo.projectCode`)。
+
+### 4.3 更新要素值
+
+```
+PUT /api/v1/projects/{projectId}/values/{elementKey}
+```
+
+**请求体**:
+
+```json
+{
+  "valueText": "新的值文本",
+  "valueJson": null,
+  "fillSource": "manual"
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| valueText | string | 文本值 |
+| valueJson | string | JSON 格式值(表格类型使用) |
+| fillSource | string | 填充来源:`manual` / `ai` / `rule` |
+
+### 4.4 批量更新要素值
+
+```
+POST /api/v1/projects/{projectId}/values/batch
+```
+
+**请求体**:
+
+```json
+{
+  "values": [
+    {
+      "elementKey": "basicInfo.projectCode",
+      "valueText": "BZ-0092-2024",
+      "fillSource": "manual"
+    },
+    {
+      "elementKey": "basicInfo.companyName",
+      "valueText": "成都院",
+      "fillSource": "ai"
+    }
+  ]
+}
+```
+
+---
+
+## 5. 附件模块
+
+### 5.1 获取附件列表
+
+```
+GET /api/v1/projects/{projectId}/attachments
+```
+
+**响应 `data`**:
+
+```json
+[
+  {
+    "id": 400,
+    "displayName": "01-附件1  成都院核心要素评审情况记录表.docx",
+    "fileKey": "ATT-001",
+    "fileName": "01-附件1  成都院核心要素评审情况记录表.docx",
+    "filePath": null,
+    "fileType": "docx",
+    "fileSize": 376919,
+    "parseStatus": null,
+    "parsedText": null,
+    "parsedAt": null,
+    "entityCount": 0,
+    "projectId": 10,
+    "sortOrder": 0,
+    "createdAt": "2026-02-13T17:11:28",
+    "updatedAt": "2026-02-13T17:11:28"
+  }
+]
+```
+
+**当前附件数据**(9 个):
+
+| sortOrder | displayName | fileType | fileSize |
+|-----------|-------------|----------|----------|
+| 0 | 01-附件1  成都院核心要素评审情况记录表.docx | docx | 376,919 |
+| 1 | 02-附件2  成都院现场评审分工表.zip | zip | 2,171,136 |
+| 2 | 03-附件3 关于深入开展电力安全生产标准化建设暨安全环保提升专项行动的通知.pdf | pdf | 360,940 |
+| 3 | 04-附件4  成都院材料真实性说明.png | png | 514,264 |
+| 4 | 05-附件5  成都院在建项目一览表.docx | docx | 16,056 |
+| 5 | 06-附件6  成都院安全管理制度清单.docx | docx | 16,942 |
+| 6 | 07-附件7  成都院现场评审末次会签到表.png | png | 564,269 |
+| 7 | 08-中国电建集团成都勘测设计工作方案.zip | zip | 299,279 |
+| 8 | 09-附件8 复审问题建议表.docx | docx | 29,034 |
+
+### 5.2 上传附件
+
+```
+POST /api/v1/projects/{projectId}/attachments/upload
+Content-Type: multipart/form-data
+```
+
+**表单参数**:
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| file | File | ✅ | 文件 |
+| displayName | string | 否 | 显示名称(默认取文件名) |
+
+**响应 `data`**:
+
+```json
+{
+  "id": 409,
+  "displayName": "新附件.docx",
+  "fileName": "新附件.docx",
+  "fileType": "docx",
+  "fileSize": 12345,
+  "parseStatus": "pending",
+  "taskId": null
+}
+```
+
+### 5.3 获取附件详情
+
+```
+GET /api/v1/attachments/{id}
+```
+
+**响应 `data`**:`AttachmentVO` 对象。
+
+### 5.4 删除附件
+
+```
+DELETE /api/v1/attachments/{id}
+```
+
+### 5.5 触发附件解析
+
+```
+POST /api/v1/attachments/{id}/parse
+```
+
+### 5.6 调整附件排序
+
+```
+PUT /api/v1/attachments/{id}/sort
+```
+
+**请求体**:
+
+```json
+{
+  "sortOrder": 3
+}
+```
+
+### 5.7 获取附件文档内容
+
+```
+GET /api/v1/attachments/{id}/doc-content
+```
+
+返回附件解析后的结构化 JSON(格式同 2.9 项目文档内容)。
+
+---
+
+## 6. 规则模块
+
+### 6.1 获取项目规则列表
+
+```
+GET /api/v1/projects/{projectId}/rules
+```
+
+**响应 `data`**:
+
+```json
+[
+  {
+    "id": 1,
+    "ruleName": "提取项目编号",
+    "elementKey": "basicInfo.projectCode",
+    "ruleType": "direct_entity",
+    "actionType": "extract",
+    "actionConfig": null,
+    "dslContent": null,
+    "description": "从附件中提取项目编号",
+    "status": "active",
+    "lastOutputText": null,
+    "lastOutputJson": null,
+    "lastRunStatus": null,
+    "lastRunTime": null,
+    "lastRunError": null,
+    "projectId": 10,
+    "inputs": [
+      {
+        "inputId": 1,
+        "ruleId": 1,
+        "sourceNodeId": 400,
+        "inputKey": "attachment",
+        "inputName": "附件来源",
+        "inputType": "attachment",
+        "fixedValue": null,
+        "sourceType": "ATTACHMENT",
+        "sourceName": "01-附件1",
+        "sortOrder": 0
+      }
+    ],
+    "createdAt": "2026-02-13T00:38:50",
+    "updatedAt": "2026-02-13T00:38:50"
+  }
+]
+```
+
+### 6.2 获取规则详情
+
+```
+GET /api/v1/rules/{id}
+```
+
+### 6.3 创建规则
+
+```
+POST /api/v1/projects/{projectId}/rules
+```
+
+**请求体**:
+
+```json
+{
+  "elementKey": "basicInfo.projectCode",
+  "ruleName": "提取项目编号",
+  "ruleType": "direct_entity",
+  "actionType": "extract",
+  "actionConfig": null,
+  "dslContent": null,
+  "description": "从附件中提取项目编号",
+  "inputs": [
+    {
+      "inputKey": "attachment",
+      "inputType": "attachment",
+      "inputName": "附件来源",
+      "sourceNodeId": 400,
+      "fixedValue": null
+    }
+  ]
+}
+```
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| elementKey | string | ✅ | 目标要素键 |
+| ruleName | string | ✅ | 规则名称 |
+| ruleType | string | 否 | 规则类型 |
+| actionType | string | 否 | 动作类型 |
+| description | string | 否 | 描述 |
+| inputs | array | 否 | 规则输入列表 |
+
+### 6.4 更新规则
+
+```
+PUT /api/v1/rules/{id}
+```
+
+**请求体**:同创建。
+
+### 6.5 删除规则
+
+```
+DELETE /api/v1/rules/{id}
+```
+
+### 6.6 执行单个规则
+
+```
+POST /api/v1/rules/{id}/execute
+```
+
+**响应 `data`**:
+
+```json
+{
+  "ruleId": 1,
+  "status": "success",
+  "outputText": "BZ-0092-2024",
+  "elementKey": "basicInfo.projectCode",
+  "valueUpdated": true,
+  "duration": 1200
+}
+```
+
+### 6.7 批量执行规则
+
+```
+POST /api/v1/projects/{projectId}/rules/execute-all
+```
+
+**响应 `data`**:`RuleExecuteResultVO[]` 数组。
+
+---
+
+## 接口总览
+
+| # | 方法 | 路径 | 说明 |
+|---|------|------|------|
+| **认证** | | | |
+| 1 | POST | `/api/v1/auth/login` | 用户登录 |
+| 2 | POST | `/api/v1/auth/logout` | 用户登出 |
+| 3 | POST | `/api/v1/auth/refresh` | 刷新 Token |
+| 4 | GET | `/api/v1/auth/me` | 获取当前用户信息 |
+| 5 | PUT | `/api/v1/auth/password` | 修改密码 |
+| 6 | GET | `/api/v1/auth/permissions` | 获取权限树 |
+| **项目** | | | |
+| 7 | GET | `/api/v1/projects` | 项目列表(分页) |
+| 8 | GET | `/api/v1/projects/{id}` | 项目详情 |
+| 9 | POST | `/api/v1/projects` | 创建项目 |
+| 10 | PUT | `/api/v1/projects/{id}` | 更新项目 |
+| 11 | DELETE | `/api/v1/projects/{id}` | 删除项目 |
+| 12 | POST | `/api/v1/projects/{id}/copy` | 复制项目 |
+| 13 | PUT | `/api/v1/projects/{id}/archive` | 归档项目 |
+| 14 | POST | `/api/v1/projects/{id}/export` | 导出项目 |
+| 15 | GET | `/api/v1/projects/{id}/doc-content` | 获取项目文档内容 |
+| **要素** | | | |
+| 16 | GET | `/api/v1/projects/{projectId}/elements` | 要素列表 |
+| 17 | POST | `/api/v1/projects/{projectId}/elements` | 添加要素 |
+| 18 | PUT | `/api/v1/projects/{projectId}/elements/{elementId}` | 更新要素 |
+| 19 | DELETE | `/api/v1/projects/{projectId}/elements/{elementId}` | 删除要素 |
+| **要素值** | | | |
+| 20 | GET | `/api/v1/projects/{projectId}/values` | 获取所有要素值 |
+| 21 | GET | `/api/v1/projects/{projectId}/values/{elementKey}` | 获取单个要素值 |
+| 22 | PUT | `/api/v1/projects/{projectId}/values/{elementKey}` | 更新要素值 |
+| 23 | POST | `/api/v1/projects/{projectId}/values/batch` | 批量更新要素值 |
+| **附件** | | | |
+| 24 | GET | `/api/v1/projects/{projectId}/attachments` | 附件列表 |
+| 25 | POST | `/api/v1/projects/{projectId}/attachments/upload` | 上传附件 |
+| 26 | GET | `/api/v1/attachments/{id}` | 附件详情 |
+| 27 | DELETE | `/api/v1/attachments/{id}` | 删除附件 |
+| 28 | POST | `/api/v1/attachments/{id}/parse` | 触发附件解析 |
+| 29 | PUT | `/api/v1/attachments/{id}/sort` | 调整附件排序 |
+| 30 | GET | `/api/v1/attachments/{id}/doc-content` | 获取附件文档内容 |
+| **规则** | | | |
+| 31 | GET | `/api/v1/projects/{projectId}/rules` | 规则列表 |
+| 32 | GET | `/api/v1/rules/{id}` | 规则详情 |
+| 33 | POST | `/api/v1/projects/{projectId}/rules` | 创建规则 |
+| 34 | PUT | `/api/v1/rules/{id}` | 更新规则 |
+| 35 | DELETE | `/api/v1/rules/{id}` | 删除规则 |
+| 36 | POST | `/api/v1/rules/{id}/execute` | 执行单个规则 |
+| 37 | POST | `/api/v1/projects/{projectId}/rules/execute-all` | 批量执行规则 |
+
+---
+
+## 快速接入示例
+
+### Axios 配置
+
+```javascript
+import axios from 'axios'
+
+const api = axios.create({
+  baseURL: 'http://47.108.80.98:8001/api/v1',
+  timeout: 30000,
+  headers: { 'Content-Type': 'application/json' }
+})
+
+// 请求拦截器:自动携带 Token
+api.interceptors.request.use(config => {
+  const token = localStorage.getItem('accessToken')
+  if (token) config.headers.Authorization = `Bearer ${token}`
+  return config
+})
+
+// 响应拦截器:统一处理
+api.interceptors.response.use(
+  res => {
+    if (res.data.code === 200) return res.data.data
+    return Promise.reject(new Error(res.data.message))
+  },
+  err => {
+    if (err.response?.status === 401) {
+      // Token 过期,跳转登录
+      localStorage.removeItem('accessToken')
+      window.location.href = '/login'
+    }
+    return Promise.reject(err)
+  }
+)
+
+export default api
+```
+
+### 典型调用流程
+
+```javascript
+// 1. 登录
+const { accessToken } = await api.post('/auth/login', {
+  username: 'admin',
+  password: 'admin123'
+})
+localStorage.setItem('accessToken', accessToken)
+
+// 2. 获取项目列表
+const projects = await api.get('/projects')
+
+// 3. 加载项目数据(并行请求)
+const projectId = 10
+const [elements, values, attachments, rules] = await Promise.all([
+  api.get(`/projects/${projectId}/elements`),
+  api.get(`/projects/${projectId}/values`),
+  api.get(`/projects/${projectId}/attachments`),
+  api.get(`/projects/${projectId}/rules`)
+])
+
+// 4. 加载文档内容
+const docContent = await api.get(`/projects/${projectId}/doc-content`)
+
+// 5. 更新要素值
+await api.put(`/projects/${projectId}/values/${encodeURIComponent('basicInfo.projectCode')}`, {
+  valueText: '新编号'
+})
+```
+
+---
+
+## 测试账号
+
+| 用户名 | 密码 | 角色 |
+|--------|------|------|
+| admin  | admin123 | 管理员 |

BIN
mock_docs/.~模板.docx


File diff suppressed because it is too large
+ 0 - 0
mock_docs/parsed_doc_full.json


Some files were not shown because too many files changed in this diff