# app-sem-fronted **Repository Path**: AndyWang66/app-sem-fronted ## Basic Information - **Project Name**: app-sem-fronted - **Description**: 这是前段后端APP端的整体项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-29 - **Last Updated**: 2026-07-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 仓库概述 本仓库是电梯物联网管理平台「智慧梯管管理系统(SEM)」的**三端工程集合**:一个 Spring Boot 后端 + 两个前端(小程序、PC Web),三端公用同一套后端接口。 ``` all/ ├── sem/ # 🔧 Spring Boot 后端(Maven 多模块,唯一可运行服务) │ ├── CLAUDE.md # 后端架构详档(模块/认证/审批/IoT,必读) │ ├── README.md # 技术栈、环境、Docker 部署 │ └── 技术架构文档.md # 审批流深度分析 + 优化方案 ├── sem_applet/ # 📱 uni-app 微信小程序(现场维保 / 维修作业 / 客户确认) ├── sem_vue-master/ # 🖥️ Vue 2 + Element UI Web 端(RuoYi-Vue 3.8.7 后台 + H5 免登录页) │ └── CLAUDE.md # Web 端架构详档(权限路由 / Vuex / 布局,必读) └── CLAUDE.md # 本文件(仓库级总览 + 跨端协作) ``` - **后端详情** → `sem/CLAUDE.md` - **Web 端详情** → `sem_vue-master/CLAUDE.md` - 小程序无独立文档,见本文「前端架构」章节 - ⚠️ **曾误称为 `frontend/`(Vue3+Vite+Pinia)的目录不存在**——PC Web 端的实际目录是 `sem_vue-master/`,技术栈见下文。 ## 快速命令 ### 后端(`sem/` 目录) ```bash # 开发环境打包(跳过测试)— 必须在 sem/ 目录内执行(多模块 reactor 根) mvn clean package -DskipTests # 生产环境打包 mvn clean package -P prod -DskipTests # 运行 java -jar sem-admin/target/sem-admin.jar --spring.profiles.active=dev # 本地调试(跳过外部依赖 EMQX/ZLMediaKit) java -jar sem-admin/target/sem-admin.jar --emqx.broker= --zlm.mediaKit-Url= --spring.profiles.active=dev ``` > ⚠️ Maven 命令必须在 `sem/` 目录内执行;在仓库根目录运行会找不到 `pom.xml`。 - **产物**: `sem-admin/target/sem-admin.jar` | **开发端口**: 8084 | **Docker 端口**: 8080 - **要求**: JDK 1.8、Maven 3.x、MySQL 5.7+(先建 `sem` 库)、Redis 5.0+ ### 小程序前端(`sem_applet/` 目录) 基于 **uni-app + Vue 3 + Vuex**,用 HBuilder X 开发,无 npm 构建脚本: ```bash npm install # HBuilder X → 运行 → 运行到小程序模拟器 → 微信开发者工具 ``` - **后端基地址**: 由 `utils/http.js` 按 `envVersion` 切换——`develop`/`trial` → `http://localhost:8084`;正式版 → `https://zn.zhitilvke.com/api` - **目标平台**: 微信小程序(appid `wx0fb6a2c3289dec52`);企业微信 appid `wwd4aac5d7c0b6e7e9` ### Web 前端(`sem_vue-master/` 目录) 基于 **Vue 2.6 + Vuex 3 + Vue Router 3(hash 模式)+ Element UI 2.15 + RuoYi-Vue 3.8.7**: ```bash cd sem_vue-master npm install --registry=https://registry.npmmirror.com npm run dev # 开发,端口 8080,代理 /test → localhost:8084 npm run build:prod # 生产构建(带 --openssl-legacy-provider,兼容 Node17+) npm run build:staging npm run lint ``` - **dev 端口**: `8080`;`vue.config.js` 把 `/test` 反向代理到后端 `localhost:8084` - **生产 API base**: `request.js` 中若 `VUE_APP_BASE_API` 非 http 开头,则前置 `location.origin + '/api'` - 既是 **PC 管理后台**(电梯/维保/合同/配件/BI),也承载 **H5 免登录页**(`/repair/quote-h5`、`/repair/order-detail-h5`,`meta.noLayout:true` 无后台外壳),供小程序 `web-view` 和微信直接打开 ## 测试 全仓库**无单元测试**:后端无 `src/test/`;`sem_applet/package.json` 无 `scripts`;`sem_vue-master` 有 scripts 但未引入测试框架。因此: - 后端构建用 `-DskipTests` - 小程序只能经 HBuilder X 运行到微信开发者工具 - Web 端可用 `npm run build:prod` 验证编译 - 新功能需手动验证:启动后端调接口,或在对应前端实测 ## Git 仓库结构(重要:两个独立仓库) `all/` 下是**两个相互独立的 git 仓库**,提交时需分别操作: | 路径 | 远程 | 跟踪内容 | |---|---|---| | `all/`(根) | `gitee.com/AndyWang66/all.git` | `sem/` + `sem_applet/`(两者改动进同一次提交) | | `all/sem_vue-master/` | `gitee.com/AndyWang66/sem_vue-master.git` | Web 端(**嵌套独立仓库**,有自己的 `.git`) | `sem_vue-master` 虽物理上位于 `all/` 下,但是独立仓库——不要把它的改动混入 `all.git` 的提交。 ## 项目架构要点 ### 后端模块依赖(详见 sem/CLAUDE.md) ``` sem-admin(入口,唯一可运行模块) ├─ sem-framework(Security、JWT、AOP、拦截器) │ ├─ sem-system(用户/角色/菜单/部门 CRUD) │ └─ sem-ladderInfo(电梯核心业务 — 最大模块) ├─ sem-ladderInfo(电梯台账、维保、合同、配件、MQTT) ├─ sem-extension(独立维修工单 + 自建审批链 + 客户确认) ├─ sem-quartz(定时任务) └─ sem-generator(Velocity 代码生成) ``` 底层所有模块 → `sem-common`(基础类、注解、工具集) ## 审批机制详解(两套,互不干涉,修改前务必确认目标) ### 机制一:sem-extension 自建报价审批链(维修工单) - **位置**: `sem-extension` 模块,核心逻辑在 `SemRepairOrderServiceImpl` - **审批对象**: 维修工单关联的报价单 `RepairQuotationOrder`(**与机制二的实体不同**) - **三级硬编码**: 项目经理(创建)→ 区域总监 → 维保总监 → 完成,**节点与角色固定,不可配置** - **状态字段**(`RepairQuotationOrder`): - `quotationStatus`: `0` 草稿 / `1` 待区域总监审批 / `2` 待维保总监审批 / `3` 已通过 / `4` 已拒绝 - `approvalLevel`: `0` 未提交 / `2` 区域总监审批中 / `3` 维保总监审批中 / `4` 全部通过 - `currentApproverId`: 当前审批人 userId(用于权限校验) - **审批流水**: `QuotationApprovalRecord`,记录 `approverRole`/`approvalResult`/`approvalComment`/`approvalTime` - **关键方法**(`SemRepairOrderServiceImpl`): - `createQuotation` — 建报价单,`status=0` - `submitQuotation` — 提交审批:`status=1, level=2, currentApprover=areaDirectorId`,发企微通知 - `auditQuotation(QuotationAuditDTO)` — **核心审批方法**: - 通过(`auditResult=1`): `level 2→3` 流转维保总监;`level 3→4` 完成后调 `checkQuotationInventory`——库存不足→工单转 `WAITING_PARTS`(等配件),充足→`WAITING_PICKUP`(待领料) - 拒绝(`auditResult=2`): 必须填备注,`status→4, level→0`,工单退回 `QUOTING`(报价中) - 权限校验: `currentApproverId` 必须等于当前登录用户 `SecurityUtils.getUserId()`,否则报"您没有权限审批此报价单" - `resubmitQuotation` — 被拒后重新提交,重置 `status=1, level=2` - **内部审批接口**(`RepairOrderController`,前缀 `/extension/repairOrder`): - `POST /quotation/clearAudit` 提交审批 - `POST /quotation/audit` 执行审批 - **通知**: 每步流转通过 `sendWxNotification` 发企微应用消息(仅提醒,非 OA 审批) - **工单状态机**: `RepairOrderStatus` 枚举(`0` 待分配 → `1` 待处理 → `2` 待维修 → `3` 维修中 → `4` 报价中 → `5` 等配件 → `6` 待领料 → `7` 已完成,另有 `8` 已挂起 / `9` 采购中 / `-1` 已取消) ### 机制一延伸:客户侧报价确认(免登录,跨三端) 机制一内部审批通过后,报价单会生成**分享链接**发给客户确认签字。这是贯穿三端的核心免登录链路,**两个前端的对应文件存在宿主-内嵌关系**: ``` 维修工单报价单(内部审批通过) │ 生成分享链接:quotationCode + shareToken ▼ 客户在微信点开链接 │ ├──▶ 进入小程序:pages/noLogin/index.vue(免登录壳页面) │ ├── 有 quotationCode + token → uni.redirectTo → 原生页 pages/repair/quotationConfirm.vue(原生签名,主路径) │ └── 仅 quotationCode → 内嵌 web 端 H5(兜底) │ webUrl = baseURL + '/#/repair/quote-h5?quotationCode=...' │ └──▶ 或直接打开 H5:sem_vue-master 路由 /repair/quote-h5 → src/views/Repair/QuoteH5.vue ``` **关系结论(noLogin/index.vue ↔ QuoteH5.vue)**:两者是同一张报价单在「小程序原生签名」与「Web H5 兜底」两种载体间的分流,**宿主-内嵌关系**。小程序 `pages/noLogin/index.vue` 兜底分支用 `` 加载的 H5 地址 `/#/repair/quote-h5`,正是 **Web 端 `sem_vue-master/src/views/Repair/QuoteH5.vue`**(hash 路由,`router/index.js` 中 `meta.noLayout:true`)。它们不是两套独立功能——`noLogin/index.vue` 是壳,`QuoteH5.vue` 是被它 `` 内嵌渲染的内容。`noLogin/index.vue` 当前**只服务报价单这一种入口**,没有工单详情(`order-detail-h5`)的转跳分支。 **报价详情实际有三套接口(按调用方区分,勿混用)**: | 接口 | 鉴权 | 调用方 | |---|---|---| | `GET /extension/repairOrder/quotation/detail/{code}` | 需登录 | 小程序内部(`getQuotationOrderDetail`)、后台 | | `GET /extension/repairOrder/quotation/shareDetail/{code}?shareToken=` | 免登录(shareToken) | **小程序原生** `quotationConfirm.vue`(`getShareQuotationDetail`) | | `GET /extension/repairOrder/quotation/no/{code}` | 免登录(无 token) | **Web 端 H5** `QuoteH5.vue`(`getQuotationDetail`) | **客户确认/状态接口**(两端共用,免登录,靠 `shareToken` 鉴权): - `POST /extension/repairOrder/quotation/customerConfirm` — 客户确认/拒绝(带 `shareToken`、`isConfirmed`、`signFileUrl`) - `GET /extension/repairOrder/customerConfirmStatus/{orderCode}` — 查确认状态(`1` 已确认 / `2` 已拒绝) **签字上传接口两端不一致(已知坑,改前必看)**: - 后端**只有** `POST /extension/repairOrder/quotation/uploadCustomerSign`(`uni.uploadFile`/FormData,文件字段 `file`,formData 带 `quotationCode`+`shareToken`) - **小程序** `API/repair.js#uploadCustomerSign` → 调 `uploadCustomerSign` ✅ 名字正确 - **Web 端** `api/repair.js#confirmSignature` → 调 `/quotation/customerConfirmSign` ⚠️ **后端无此接口**,Web H5 的签名上传链路当前是断的;改 Web 端签名时务必对齐到 `uploadCustomerSign` **模式控制**:QuoteH5.vue 用 `query.view` 区分——`view=view` 为纯查看(显示「分享给微信好友」按钮,复制带 `view=sign` 的链接);无 `view` 则显示「确认/拒绝报价」按钮。`mounted` 时还会动态注入 OG meta 标签,优化微信分享卡片。 > 同款 `OrderDetailH5.vue`(路由 `/repair/order-detail-h5`)是工单详情的免登录 H5,同样 `noLayout`,可被小程序 web-view 加载。 ### 机制二:sem-ladderInfo 企业微信 OA 审批(维保工单报价) - **位置**: `sem-ladderInfo` 模块,`SemWXServiceImpl` + `SemQuotationOrderServiceImpl` - **审批对象**: 维保工单报价单 `SemQuotationOrder`(**与机制一的实体不同**) - **审批流不在本系统内执行**——委托给企业微信 OA 审批系统,审批人节点由**企微后台模板配置**决定(`use_template_approver=1`) **提交流程**(`SemQuotationOrderServiceImpl.clearAuditByWXApprove`): 1. `semWXService.accessToken(app)` 获取企微 access_token(Redis 缓存) 2. `gettemplatedetail` 拉取审批模板;`getTempId` 按控件标题(项目/电梯/故障描述/维修费用/创建时间/工单详情)映射出控件 id 3. `applyEvent`(企微 `/oa/applyevent`)提交审批申请,填入表单内容 4. 成功返回 `sp_no` → 写入 `SemQuotationOrder.spNo`,工单状态置 `3`(审批中) - ⚠️ 企微调用失败时**不回滚业务操作**(代码注释明确) **回调流程**(企微 → 本系统): - **入口**: `SemCallbackController` `@Anonymous` `/callback/{app}`(企微回调 URL,免登录) - 带 `echostr` → `CallBackManager.verifyUrl` 校验 URL(企微 URL 验证) - 带 xml → `CallBackManager.handle` 解密 → `semWXService.callbackHandle` - `callbackHandle` 按 `event` 分发:`sys_approval_change`(审批状态变化)→ `sysApprovalChange` - `sysApprovalChange` 校验 `templateId` 匹配后调 `appQuotationApproveCallBack` - `appQuotationApproveCallBack`: 按 `sp_no` 查报价单,取审批记录最后一条 `detail`: - `spStatus 2`=已通过 → `auditStatus="2"`,调 `auditQuotation` 生成出库单 - `spStatus 3`=已驳回 → `auditStatus="-1"` - **企微申请单状态**: `1` 审批中 / `2` 已通过 / `3` 已驳回 / `4` 已撤销 / `6` 通过后撤销 / `7` 已删除 / `10` 已支付 ### 易混淆点(修改前必读) - 两套机制各有独立的"报价单"实体(`RepairQuotationOrder` vs `SemQuotationOrder`)和"审批状态"字段,**不可混用** - 审批人来源不同:机制一由工单上的 `areaDirectorId`/`maintenanceDirectorId` 决定;机制二由企微 OA 模板配置决定 - 机制一的 `sendWxNotification` 只是发企微**应用消息提醒**,不是 OA 审批;机制二才是真正的 OA 审批委托 - 机制二回调接口 `@Anonymous` 免登录,权限由企微签名校验(`CallBackManager`)保证 - 机制一的报价详情有**三套接口**:登录版 `/quotation/detail/{code}`(后台/小程序内部)、免登录带 token 版 `/quotation/shareDetail/{code}`(小程序原生 `quotationConfirm.vue` 用)、免登录无 token 版 `/quotation/no/{code}`(Web H5 `QuoteH5.vue` 用),勿混用 - **签字上传接口两端不一致**:后端只有 `/quotation/uploadCustomerSign`;小程序对齐正确,Web 端 `api/repair.js#confirmSignature` 仍指向不存在的 `/quotation/customerConfirmSign`,是已知待修项 ## 前端架构 ### 小程序前端(`sem_applet/`) ``` pages/ — 按业务模块组织(pages.json 共 21 页) task/repair/partsChange/annualInspection/mtRecord(维保维修作业) + index/myCenter/message/login + noLogin(免登录中转) API/ — 6 个模块文件:home.js、mtTask.js、repair.js、parts.js、lift.js、annualInspection.js store/ — Vuex 单文件 createStore(state: token + userInfo;无 modules 拆分) utils/ — HTTP 封装(http.js:请求拦截、401 自动续登、HMAC-SHA256 签名) ``` - **4 个 tabBar**: 首页(index)、工作台(task/taskList)、消息(message)、我的(myCenter) - Token 过期自动续登:401 → HMAC-SHA256 签名(`username|userId|timestamp|nonce|SECRET_KEY`)→ `POST /autoLogin` → 重放原请求 - 所有 API 请求通过 `utils/http.js` 的 `axios()` 方法发起;`baseURL` 按 `uni.getAccountInfoSync().miniProgram.envVersion` 切换——`develop`/`trial` → `http://localhost:8084`;正式版 → `https://zn.zhitilvke.com/api` - `pages/noLogin/index.vue` 是免登录中转页(见上文「客户侧报价确认」),既可跳原生 `pages/repair/quotationConfirm.vue`,也可 web-view 加载 Web 端 QuoteH5.vue - `pages/repair/quotationConfirm.vue` 用 `l-signature` 组件做手写签名,是客户确认报价的**原生主路径**(调 `getShareQuotationDetail` + `uploadCustomerSign` + `customerConfirm`) ### Web 前端(`sem_vue-master/`,详见其 CLAUDE.md) 基于 **RuoYi-Vue 3.8.7** 的 Vue 2 后台,权限路由**后端驱动**(登录后 `GET /getRouters` 返回路由树,`store/modules/permission.js` 的 `loadView` 动态加载)。 - **Vuex 模块**: `app` / `user` / `permission` / `settings` / `tagsView` / `dict` - **请求层**: `src/utils/request.js`(Axios,请求附 Bearer token + 防重提交,响应按 code 分流:401 重登、500/601 提示) - **布局**: `src/layout/index.vue`;`meta.noLayout:true` 的路由(H5 页)只渲染 ``,无侧边栏/导航 - **白名单(免登)**: `/login`、`/register`、`/repair/quote-h5`、`/repair/order-detail-h5` 等 - **API 模块**: `src/api/`(`system/`、`monitor/`、`lift/`、`partsManagement/`、`bi/`、`tool/` + 顶层 `repair.js`、`contract.js` 等) - **业务视图**(`src/views`): `lift`(电梯)、`liftMt`(维保)、`weixiu`(维修/报价审批)、`Repair`(**H5 免登录页 QuoteH5 / OrderDetailH5**)、`Contract`、`SIoT`(节能)、`partsManagement`、`liftAnnualInspection`、`Organizational`、`system`、`monitor`、`bi`、`dashboard` - **构建注意**: prod/staging 构建需 `NODE_OPTIONS=--openssl-legacy-provider`(旧 webpack + Node 17+ 的 OpenSSL 3 兼容问题) ### 三端如何公用一个后端 - **后端** `sem/` 对外提供 `/sem/...`、`/extension/...`、`/system/...`、`/ladderInfo/...` 等接口 - **小程序** `sem_applet` 与 **Web 端** `sem_vue-master` 都调这些接口(基地址同为 `https://zn.zhitilvke.com/api`);Web 端生产构建产物与后端同域部署 - 客户侧免登录场景下,小程序通过 web-view 把 Web 端 H5 当作"渲染组件"复用,三端在「维修报价单客户确认」这一业务上交汇 ## 子文档指引 - `sem/CLAUDE.md` — 后端模块、认证、两套审批、IoT 通信(后端日常主要参考) - `sem_vue-master/CLAUDE.md` — Web 端权限路由、Vuex、布局、API 层(Web 端日常主要参考) - `sem/技术架构文档.md` — 审批流深度分析与优化方案