SCSAI PLM 前端集成方案 — Shim 架构

SCSAI PLM 前端集成方案 — Shim 架构

概述

将 SCSAI Agent PLM 的前端能力集成到任意 Web 应用中,无需加载 SCSAI 原生的 30+ 个 JS 文件(~2MB),只需一个 5KB 的存根(shim) + 一个 Node.js 代理路由


架构

┌────────────────────────────────────────────────────────┐
│                   你的 Web 应用                          │
│                                                        │
│  Vue 视图                                              │
│  ┌─────────────────────┐    直接调用                    │
│  │ CustomerManagement  │ ───→ window._bomService       │
│  │ InventoryManagement │      ._SCSAIApiRequest()       │
│  │ OrderManagement     │         ↓                     │
│  │ Dashboard           │     fetch(/SCSAI-api/)         │
│  └─────────────────────┘         ↓                     │
│                         ┌────────────────┐             │
│  index.html             │ Node.js 服务端  │             │
│  ┌──────────────┐       │ (你的后端)      │             │
│  │ SCSAI-shim    │       │                │             │
│  │ (inline)     │       │ /SCSAI-api/     │────→ SCSAI   │
│  │              │       │ → Agent    │     Server  │
│  │ Sciot 存根   │       │   Server.aspx  │             │
│  │ MD5Util      │       │                │             │
│  │ IomFactory   │       │ AuthHeaders:   │             │
│  │ CacheUtil    │       │ AUTHUSER       │             │
│  └──────┬───────┘       │ AUTHPASSWORD   │             │
│         │               │ DATABASE       │             │
│         ↓               └────────────────┘             │
│  index.js                                             │
│  (SCSAIBomService)                                     │
│  4005行 · 保留不变                                    │
└────────────────────────────────────────────────────────┘

数据流

Vue 组件
  → window._bomService._SCSAIApiRequest('ApplyItem', aml)
    → fetch('/SCSAI-api/', {
        method: 'POST',
        headers: { AUTHUSER, AUTHPASSWORD (MD5), DATABASE },
        body: aml
      })
      → 你的 Node.js 服务端 /SCSAI-api/ 路由
        → http.request → http://SCSAI-server/InnovatorServer.aspx
          → XML 响应
            → _parseAMLResponse() 解析为 JSON
              → 返回给 Vue 组件

整条链路不经过任何 SCSAI 浏览器端 JS 文件


集成步骤(5 分钟)

1. 复制以下文件到你的项目

你的项目/
├── index.html                      # 入口(只需 4 个 script)
├── index.js                        # SCSAIBomService(4005 行,不动)
├── utils/
│   └── md5.js                      # MD5 哈希(242 行)
├── src/
│   ├── SCSAI-shim.js                # Vue 模块加载的额外存根(后备)
│   └── main.js                     # import './SCSAI-shim.js'
└── server.js (或你的后端)           # 需要 /SCSAI-api/ 代理路由

2. index.html 模板

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <!-- 1. MD5(必须,用于密码哈希) -->
  <script src="./utils/md5.js"></script>

  <!-- 2. SCSAI 存根(必须,在 index.js 之前加载) -->
  <script>
  // ── Sciot 最小存根 ──
  function SciotStub() {
    this.commonProperties = {
      loginName: '', password: '', database: 'SCPLM',
      userID: '', identityList: [], serverBaseURL: ''
    };
    this.varsStorage = {};
  }
  SciotStub.prototype.SetupURLs = function(url) {
    try { var m = url.match(/^(https?:\/\/[^/]+)\/([^/]+)\//);
      if (m) this.commonProperties.serverBaseURL = m[1]+'/'+m[2]+'/server/';
    } catch(e){}
  };
  SciotStub.prototype.validateUser = function() {
    return { then: function(r) { r({ ok: false, message: 'shim' }); } };
  };
  SciotStub.prototype.logout = function() {
    this.commonProperties.loginName = '';
    this.commonProperties.password = '';
  };
  window.Sciot = window.Sciot || SciotStub;
  window.sciot = window.sciot || new SciotStub();

  // ── 其他全局存根 ──
  window.IomFactory = window.IomFactory || {
    CreateAgent: function(){ return {
      apply: function(){ return {getResult:function(){return''},isError:function(){return false}} },
      getNewItem: function(){ return {setProperty:function(){},setID:function(){},
        apply:function(){return{isError:function(){return false}}}} }
    } }
  };
  window.SciotModules = window.SciotModules || {
    soap: function(){ return {status:200,responseText:'<Envelope><Body><Result></Result></Body></Envelope>'} },
    Dialog: { show: function(){ return {promise:Promise.resolve()} },
              alert: function(m){ alert(m) },
              confirm: function(m){ return confirm(m) } }
  };
  window.CacheUtil = window.CacheUtil || function(){
    this.get=function(){}; this.set=function(){};
    this.generateKey=function(){return''};
    this.wrap=function(k,fn){return fn()};
  };
  console.log('[SCSAI-shim] 存根就绪');
  </script>

  <!-- 3. SCSAIBomService(核心业务逻辑,4005 行) -->
  <script src="./index.js"></script>
</head>
<body>
  <div id="app"></div>
  <!-- 4. Vue 应用入口 -->
  <script type="module" src="/src/main.js"></script>
</body>
</html>

3. Node.js 服务端 — /SCSAI-api/ 代理

在你的后端(Express / Fastify / Node http)中添加:

// /SCSAI-api/ 代理 — 转发 AML 到 SCSAI InnovatorServer.aspx
app.post('/SCSAI-api/', async (req, res) => {
  const username = req.headers['authuser'];
  const password = req.headers['authpassword']; // MD5 哈希
  const database = req.headers['database'] || 'SCPLM';
  const aml = req.body;

  const SCSAIUrl = new URL('https://ylxt.chat/scplm/server/InnovatorServer.aspx');

  const options = {
    hostname: SCSAIUrl.hostname,
    port: SCSAIUrl.port || 80,
    path: SCSAIUrl.pathname,
    method: 'POST',
    headers: {
      'Content-Type': 'text/xml',
      'Content-Length': Buffer.byteLength(aml),
      'SOAPAction': 'ApplyItem',
      'AUTHUSER': username,
      'AUTHPASSWORD': password,
      'DATABASE': database
    },
    timeout: 30000
  };

  const proxyReq = http.request(options, (proxyRes) => {
    let body = '';
    proxyRes.on('data', chunk => body += chunk);
    proxyRes.on('end', () => {
      res.writeHead(proxyRes.statusCode, {
        'Content-Type': 'text/xml; charset=UTF-8'
      });
      res.end(body);
    });
  });
  proxyReq.on('error', (e) => {
    res.status(500).json({ error: e.message });
  });
  proxyReq.write(aml);
  proxyReq.end();
});

4. 前端登录流程

Vue 组件中调用登录:

const credentials = {
  username: 'root',
  password: 'gyc123456;',
  database: 'SCPLM',
  serverUrl: 'https://ylxt.chat/scplm/server'
};

// 方式一:使用 SCSAIBomService(推荐)
const result = await window._bomService.login(credentials);

// 方式二:直接 AML 调用
const aml = '<AML><Item type="Part" action="get" select="id,name" pagesize="5"/></AML>';
const data = await window._bomService._SCSAIApiRequest('ApplyItem', aml);

文件职责

| 文件 | 大小 | 职责 | 必须? |

|------|------|------|--------|

| index.html 内联存根 | ~2KB | 定义 Sciot/IomFactory/CacheUtil 等全局对象 | |

| utils/md5.js | 242行 | MD5 哈希(SCSAI 认证用 AuthHeaders MD5 mode) | |

| index.js | 4005行 | SCSAIBomService 类:_SCSAIApiRequestlogin、CRUD | |

| src/SCSAI-shim.js | ~3KB | Vue 模块加载时的后备存根 | 推荐 |

| server.js /SCSAI-api/ 路由 | ~30行 | 转发 AML → SCSAI | |


原始文件 vs Shim 对照

原始 SCSAI 文件大小替代方式
mscorlib.js1行 (min)不需要
IOM.ScriptSharp.debug.js4334行window.IomFactory 空对象
core/sciot_object.js6938行 (218KB)SciotStub 构造器 + 5 个方法
sciot_user.js893行SciotStub.prototype.validateUser
sciot_modules.js149行window.SciotModules 空对象
core/GlobalObjectsPatch.js879行已内联进 inline 存根
cryptoJS.js + md5.js + cryptohash.js多文件utils/md5.js(保留)
其余 20+ 文件 (Utils/Soap/XML/UI)大量全部不需要

关键约束

加载顺序(必须严格遵守)

1. utils/md5.js        ← 提供 window.MD5Util
2. inline shim         ← 提供 window.Sciot / IomFactory 等
3. index.js            ← window.SCSAIBomSkill = new SCSAIBomService()
4. /src/main.js        ← Vue 挂载(import SCSAI-shim.js 作为后备)

MD5 哈希必须与 SCSAI 兼容

SCSAI Agent 使用 MD5 对密码进行哈希后通过 AUTHPASSWORD 头发送。必须使用与 utils/md5.js 一致的实现:

// 正确(项目已验证)
MD5Util.hash('gyc123456;') === '0d1d076e1b9baa7a04f7ad9b315296f9'

// 错误(字节序不对的 MD5 会产生错误的哈希,认证会失败)

SCSAIBomService 不变

index.js 是一个 4005 行的自包含类,定义 window.SCSAIBomServicewindow.SCSAIBomSkill不要修改它。它的 _ensureSciotMethods() 会在 Sciot.prototype 上补丁 login/getPassword/getLoginName 等方法,前提是 window.Sciot 已定义。


在数字员工平台中集成

推荐目录结构

数字员工平台项目/
├── plm/
│   ├── index.html          # PLM 模块入口(含 inline shim)
│   ├── index.js            # SCSAIBomService(从 bossagents 复制)
│   ├── utils/
│   │   └── md5.js          # 从 bossagents 复制
│   ├── src/
│   │   ├── SCSAI-shim.js    # 后备存根
│   │   ├── plm-router.js   # PLM 相关路由
│   │   └── views/          # PLM 视图(CustomerManagement 等)
│   └── server/
│       └── SCSAI-proxy.js   # /SCSAI-api/ 代理路由
├── digital-staff/          # 你现有的数字员工模块
└── package.json

PLM 功能清单(可复用的 Vue 视图)

| 视图 | SCSAI ItemType | 功能 |

|------|---------------|------|

| CustomerManagement.vue | Customer | 客户 CRUD |

| VendorManagement.vue | Vendor | 供应商 CRUD |

| OrderManagement.vue | Order | 订单 CRUD |

| InventoryManagement.vue | Inventory | 库存管理 |

| ChangeManagement.vue | ECR/ECO | 变更管理 |

| QualityManagement.vue | Quality | 质量管理 |

| ProjectManagement.vue | Project | 项目管理 |

| BomAssistant.vue | Part / BOM | BOM 对比、分析 |

| Dashboard.vue | 多类型 | 聚合看板 |

所有视图统一使用:

const result = await window._bomService._SCSAIApiRequest('ApplyItem', aml);

排错

| 错误 | 原因 | 修复 |

|------|------|------|

| this.validateUser is not a function | SciotStub 未定义 validateUserindex.js 先于 shim 加载 | 确保 inline shim 在 index.js 之前 |

| Authentication failed for root | MD5 哈希算法错误或密码错误 | 检查 MD5Util.hash() 输出是否正确 |

| CacheUtil is not a constructor | window.CacheUtil 未定义 | 在 inline shim 中添加空构造器 |

| SOAP Fault / AML 错误 | AML 语法或权限问题 | 检查 SCSAI 服务器日志 |

| _bomService 未定义 | index.js 未加载或加载顺序错误 | 检查

🤖
🎁