# 门窗 Saunders 云平台(windoorcraft.com 同功能系统)克隆实施方案 > 版本:v0.1 | 日期:2026-09-20 | 编制:HHY > 目标站:https://www.windoorcraft.com/home (代号 zzc,出品方 thinkerx.com) > 本文档基于对目标站的**只读接口采集**(用户自有账户)与前端结构分析编写,所有代码实现均为自研原创。 --- ## 1. 目标产品画像 windoorcraft.com 是一套面向门窗行业的 SaaS 工具集,核心价值链: ``` 画窗(拖拽布置分格) → 自动算料报价 → 客户确认(合同/订单) → 生产(优化下料/打印) → 财务(收付款) → 库存 ``` 已探明的子系统: | 子系统 | 说明 | 证据(路由/接口) | |---|---|---| | 2D 画窗编辑器 | 拖拽布置外框、中梃、扇、玻璃、横竖撑,参数化尺寸 | `/draw`、`drawing_data`、`shapeManager` | | 报价引擎 | 自动报价 + 手动报价,型材/玻璃/五金附件计价 | `/manual-quote`、`automatic_quote`、`base_price`、`offer-price` | | 订单/合同 | 意向单→合同→订单流转,电子签名确认 | `/orders`、`contracts`、`convert-contract/`、`up-contract-confirmed/` | | 客户管理 CRM | 客户档案、业务员、客户分级 | `/customers_list`、`salesmen`、`ywy-*` | | 生产管理 | 订单进度、车间派工、条码打印 | `get_order_process`、`printer`、`order_format` | | 优化下料 | 一维型材优化切割(BIN 问题) | `optimize1d`、`cut_solution`、`contract_bars` | | 库存/仓库 | 型材/玻璃/配件库存、出入库 | `warehouse_list`、`product_move_in/`、`materials_config` | | 3D/渲染 | BabylonJS 3D 展示、DXF 出图、AI 炫图 | `/threed`、`renderV2`、`sketch-to-window-info` | | 样板库 | 254 套官方窗型模板(6 大分类) | `search_default_windoor`、`default_windoor_info/{id}` | | 商城/会员 | 软件订阅购买、试用、邀请返利 | `/purchase`、`company-module-info`、`invited_user_list` | | 多端生态 | 微信小程序/公众号/APP、Apple/Google 登录 | `wechat-login-with-phone`、`wx/getwxurllink`、`apple_login` | | 多语言 | 中/英/韩/俄/越 5 语言包 | `lang-en-us` 等独立 chunk | | 子账户/权限 | 主子账户、业务员/经销商角色 | `son_user_update`、`up_son_user_permission`、`dls-*` | ## 2. 已采集数据资产(本次交付) 全部存于 `data/` 目录: | 文件 | 内容 | 规模 | |---|---|---| | `api-endpoint-inventory.txt` | 从前端提取的全部接口名清单 | ≈300 条 | | `api-samples/*.json` | 16 个核心接口的真实响应样本 | — | | `api-samples/windoor-templates-clean.json` | **样板库全量清单**(含分类名映射) | 254 条 | | `windoor-drawings/{id}.json` | **每套模板的完整绘制数据 `drawing_data`** | 254 个文件 / 5.4MB | | `full-site/get/` + `get-manifest.json` | **整站 GET 扫描**:279 个接口逐一探测留档(200→90,业务成功 76) | 全量 | | `full-site/post/` + `post-manifest.json` | **POST 安全探测**:24 个只读/计算语义接口(付费墙/参数契约确认) | 全量 | | `full-site/sweep_get.py` / `sweep_post.py` | 可复用的限速采集脚本(白名单制,危险接口永不触碰) | — | > 订单模块受目标站付费墙保护(`code=10086`),列表/详情数据无法以试用水印账户获取;但订单写入接口的**完整请求契约**已通过源码静态分析拿到:创建订单 `POST orders/{contract_id, drawing_data, pic_url}`、详情 `GET orders/{id}`、排序 `PUT orders/{id}`、删除 `DELETE orders/{id}`。自研实现不依赖对方付费墙数据。 ### 2.1 样板库统计 | 分类 | 数量 | |---|---| | 平开 | 99 | | 转角窗 | 54 | | 推拉 | 35 | | 复古门 | 31 | | 肯德基门 | 27 | | 中式门窗 | 8 | | **合计** | **254** | > 说明:`search_default_windoor` 接口无分页(前端仅传 `name` 关键字参数),一次返回全量;经验证 `page` 参数被忽略、三页内容完全一致,故 **254 条即官方样板库全量**,本次已 100% 采集成功(254/254)。 ### 2.2 drawing_data 数据模型(拖拽编辑器核心,已破解) `default_windoor_info/{id}` 返回的 `drawing_data` 是编辑器画布的完整序列化状态,直接喂给 `canvas.shapeManager.openFile()`。结构: ``` drawing_data ├── s 全局设置 { cs: 外框默认料宽60, cjs: 100 } ├── cp 预设参数 ├── sm[] 形状集合(多形状 = 转角窗/连体窗) │ └── 每个形状: │ ├── cm 颜色映射 { g: 玻璃填充色, bn: 边框色, hd: 高光, ben: } │ ├── dm 尺寸标注 { dims: [{at, ds, eidx, midx, name, type: line|mullion}] } │ ├── fm 外框模型 { ew: [左,上,右,下]料宽, vs: 顶点, ejw, pew, ast } │ ├── mm 中梃模型 { sp: [分割线 {line:{pt,norm}, alignType, equalSplit, │ │ kw(竖撑), ws(窗撑), reinforced, polyId}] } │ ├── sashes[] 扇:开向 openToward、开启状态 opened、带转框 withTurningFrame │ ├── bars / bds 格条(防盗格/装饰格) │ ├── gg / gcs 玻璃配置 │ ├── ut / tcs 推拉窗轨道(emptyTrackAppended) │ ├── rt / ep 圆弧半径(复古门弧顶) │ └── polygon 多边形轮廓(非矩形窗) ├── epi / sbf / wlc 编辑器状态(点吸附/选中框/墙线) ├── wall 附墙信息 └── scale 缩放比例 ``` 关键结论:**编辑器 = "多边形外框 + 中梃分割线树 + 区域→扇/玻璃分配" 的三层模型**。分割线(sp)带单位向量 norm 与对齐策略,渲染时递归切割多边形得到玻璃区域。这就是拖拽布置搭建的实现基础。 ## 3. 目标技术架构(自研实现) ### 3.1 技术选型 结合本仓库已有的 Python 骨架(`pyproject.toml`,Python ≥3.14): | 层 | 选型 | 说明 | |---|---|---| | 后端 | **FastAPI + SQLAlchemy 2.0 + MySQL 8** | 异步、类型安全(符合类型安全原则)、自带 OpenAPI 文档 | | 前端 | **Vue 3 + TypeScript + Element Plus + Pinia** | 与原站 Vue 系心智一致;画布层自研 | | 画布引擎 | **原生 Canvas 2D + 自研 shapeManager**(复刻数据模型 §2.2) | 备选 Konva.js 减少底层工作量 | | 3D | Three.js + DXF 导出库 | 原站用 BabylonJS,功能等效替换 | | 缓存/队列 | Redis + Celery(优化下料、渲染走异步任务) | optimize1d 计算密集 | | 文件存储 | 阿里云 OSS / MinIO(自托管备选) | 原站用阿里云 OSS + 华为云 OBS | | 认证 | JWT (HS256, 24h) + 手机验证码 + 微信 OAuth | 对齐原站鉴权方式 | ### 3.2 模块划分(严格分层) ``` src/huamenchuang/ ├── main.py # FastAPI 入口、前端静态挂载 ├── core/ # 配置、安全(JWT)、依赖注入、统一响应封装 │ ├── config.py security.py response.py # {code,message,data} envelope ├── models/ # SQLAlchemy ORM(见 §4 领域模型) ├── schemas/ # Pydantic 请求/响应模型 ├── repositories/ # 数据访问层 ├── services/ # 业务逻辑层 │ ├── auth_service.py customer_service.py contract_service.py │ ├── order_service.py product_service.py warehouse_service.py │ ├── quote_service.py optimize_service.py template_service.py ├── api/ # Controller 路由层(按模块拆分 router) └── utils/ # 一维优化算法、公式引擎、单位换算 frontend/ ├── src/ │ ├── api/ # axios 封装 + 接口定义 │ ├── stores/ # Pinia │ ├── views/ # 与原站 42 条路由对应的页面 │ └── components/canvas/ # shapeManager / Frame / Mullion / Sash / Glass / Dimension └── dist -> 后端静态挂载 # 构建产物挂载(符合前后端规范) ``` ### 3.3 拖拽布置编辑器(核心难点)复刻方案 —— v2 已实现 **原站设计思路**(逆向自 drawing_data 序列化结构,经 254 套样本验证): 1. **三层模型**:`Frame 形状 = 基底多边形(polygon.polygon) + 分割树(mm.sp[]) + 扇子形状(sm[])` 2. **分割树**(关键发现):`polygon.polygon` 不是最终窗格,而是基底多边形;`mm.sp[]` 按序记录每次分割——`line{pt,norm}` 定位分割线、`polyId{idx,pos}` 指向作用区域与插入位。编辑器打开文件时**重放分割树**得到最终窗格。同一中梃可能被记录为多条共线分段(随区域生长拆分) 3. **扇模型**:子形状 `Sash/DoubleSash`,`sashes[]` 描述子扇(`od` 开向角、`wtf` 转框、`openToward` 内开/外开),经 `polyId` 挂到窗格;`bd` 边界在序列化中常退化,运行时重算 4. **序列化即状态**:drawing_data 直接喂给 `shapeManager.openFile()` 恢复整个画布 **自研实现**(`frontend/src/components/WindoorCanvas.vue`,约 700 行,Canvas2D): | 能力 | 实现 | |---|---| | 真实网格渲染 | `replayShape()` 分割树重放(两次交叉点法切多边形,退化/非凸跳过),254 套样板全部还原真实分格(平开 4-7 格、推拉亮子结构、转角窗多体) | | 中梃绘制 | 每条 sp 按其作用区域 bbox 裁剪为分段双线;共线分段自然重叠无害 | | 扇样式推断 | `有效bd → polyId 映射区域(双扇对半) → 帧bbox按扇数竖向均分` 三级解析;开向三角/内开空心/门标注 | | 尺寸参数化 | W/H 输入框 + 拖拽外框右缘/下缘/右下角手柄;全部几何(含 sp 点位)按比例缩放,料宽不变 | | **加中梃** | +竖梃/+横梃 → 点击玻璃区域 → 追加 sp(polyId.idx=被点窗格)→ 重放 | | **改中梃** | 点击选中(蓝色高亮)→ 拖动(line.pt 更新→重放,30mm 边距钳制)→ Del/按钮删除(移除 sp→重放) | | 撤销/自动报价 | JSON 快照栈;变化后 600ms 防抖调 offer-price 实时显示总价 | | 服务端算料 | Python 侧同语义 `replay_shape`:玻璃面积=重放窗格面积和,中梃长度=作用区域投影(精确到实际跨度) | 数据兼容性:编辑仅增删改 sp 与几何点,不改变 drawing_data 的字段结构——保存后仍是标准 shapeManager 格式。 ## 4. 领域模型(依据真实响应样本归纳) | 实体 | 关键字段(来自采集样本) | |---|---| | users | id, nickname, phone_number, wechat_id, union_id, apple_id, company_id, money, bounty, try_expiration_time, son_account_type, language, is_lose | | companies | id, name, 参数集(find/update_company_param_value) | | customers | id, name, phone, address, note, sort, type, salesmen, discount, balance, level, join_date | | contracts | id, contract_no(如 FG20260818009), customer_id, area, count, money, status(意向单…), cut_status, sign_status, sign_picture_url, contract_type, confirmed, discount, agency_money, oids[] | | orders | (POST 接口,结构待浏览器抓包补全) order_no, contract_id, products[], process | | windoor_templates | id, name, pic_url, catelog_id, note, times, low/high_width/height, drawing_data | | default_windoor_catelogs | 树:推拉门{平开,推拉,复古门,肯德基门,中式门窗,转角窗} | | product_catelogs | 树,is_reference, lang | | warehouses(材料) | bom_code, name, material(profile/glass/...), cost, price, color, pricing_method, length, count, warning, unit, meter_weight, thick, agency_price | | receipt_accounts | type, name, number, bank, pic_url, is_default, balance | | announcements | name, desc, pic_url, content(富文本) | | company_modules | name, type, icon, description, price, try_days, label, info | | operation_logs / invited_users | 分页日志/邀请返利 | **接口响应统一 envelope**:`{code: 1=成功, message, data, other_data, other_data2}`;分页为 Laravel 风格 `current_page/data[]/total/per_page`。自研系统沿用此规范以保持前端迁移成本最低。 ## 5. 接口清单(详见 doc/API.md) 按模块归组(GET 已验证 ✅ / POST 待抓包 ⏳): - 认证:`login` `wechat-login` `wechat-login-with-phone` `code-login` `register-and-login` `verification-code-new` `captcha` `forget-password` - 用户:`current-user`✅ `users`✅ `current-user/password` `current-user/phone` `son_user_update/` `up_son_user_permission` - 客户:`customers_list`✅ `customers` `customer_record(s)` `customers_excel_in` - 合同/订单:`contracts`✅ `convert-contract/` `orders/`(POST)⏳ `order-by-pid` `get_order_process` `up_order_no/` `contract_receipt*` - 样板库:`search_default_windoor`✅ `default_windoor_catelog`✅ `default_windoor_info/{id}`✅ `default_windoor/` `windoor/`(用户自存窗型) `windoor_template_sort` - 产品:`product-catelog`✅ `all-product`✅ `product/` `search-product` `dls-price-config` `team-discount` - 仓库:`warehouse_list`✅ `warehouse_info` `receipt_accounts`✅ `product_move_in//out/` `materials_config` - 下料:`optimize1d` `cut_solution(_new)` `contract_bars(_with_stock)` `up-cut-status` - 渲染/3D:`rendering-info` `three-d-data`(POST)⏳ `drawing_data_by_oid/` `sketch-to-window-info` - 财务:`contract_receipt_list`✅样 `contract_receipt_statistics(_tb)` `withdraw-money` `pay-order-list` - 平台:`announcements`✅ `company-module-info`✅ `get-showdoc-menu`✅ `feedback` `ip_to_region` > ⏳ 项:写入类接口(POST/PUT)为避免对生产系统产生副作用未做探测,需要在自研时按功能自行定义,或后续用浏览器 DevTools Network 面板在实际操作时抓包补全。 ## 6. 里程碑计划 | 里程碑 | 内容 | 验收 | |---|---|---| | M1 基础框架 | FastAPI 骨架/统一响应/JWT 认证/Vue3 骨架/部署脚本 | 登录+用户中心跑通 | | M2 业务 CRUD | 客户/合同/订单/产品/仓库 + Excel 导入导出 + 权限 | 全部 ✅ 接口对等实现 | | M3 样板库+画图编辑器 | 拖拽布置、中梃分割、扇/玻璃指定、尺寸标注 | **加载全部 254 个采集样本渲染一致** | | M4 报价+算料 | 价格脚本引擎、自动报价、手动报价 | 与原站同窗型报价误差为 0 | | M5 生产+下料+打印 | 订单进度、optimize1d、打印模板 | 优化利用率 ≥ 原站 | | M6 生态 | 微信登录/支付、商城、邀请返利、多语言 | 对齐原站 | ## 7. 风险与合规 1. **只仿功能,不搬代码/素材**:功能逻辑与数据模型不受版权保护,但对方源码、图标、宣传图文受保护。本项目全部自研,采集数据仅作开发参照;官方样板库图片存于对方 OSS(`webcc-pro.oss-cn-hangzhou...`),上线前需替换为自绘素材或取得授权。 2. **采集凭据时效**:本次使用的 Bearer token 有效期至 2026-09-21 06:05;账户试用期至 2026-09-23。后续采集需重新登录获取 token。 3. **生产 API 礼貌访问**:所有采集均为只读且 0.3s 限速;不触碰写入接口、不触发异常路径(其报错响应会暴露框架堆栈,属对方运维问题,勿利用)。 4. **待补全项**:订单/报价写入接口的请求体结构(需浏览器抓包);价格脚本语言文法(需实际配置产品后观察)。 ## 8. 下一步(按优先级) 1. M1 搭建 FastAPI + Vue3 骨架,落统一响应与 JWT 2. 导入采集数据:`windoor_templates`、`default_windoor_catelogs` 建表入库(数据已就绪) 3. 画布 shapeManager 原型:先实现"读取 254 个样本→静态渲染",再补交互 4. 用户在真实环境抓包补全 ⏳ 接口后,更新 `doc/API.md`