resolve: 解决合并冲突

This commit is contained in:
wuzhen committed 2025-07-10 18:02:35 +08:00
commit 80dface225
97 files changed
+4718 -1615

No files matched your search

+164 -367
View File
@@ -1,386 +1,105 @@
# Cursor Rules - Web 前端开发规范
# Vue 3 + TypeScript 前端开发规范
## 适用范围
## 技术栈规范
- Vue 3.5.13
- TypeScript 5.8.3
- Element Plus UI
- Art Design Pro UI 设计规范
- **项目目录**: `web/` (基于 Vue 3 + TypeScript + Element Plus)
- **开发环境**: Node.js 20.15.x+, npm 10.7.x+
- **前端访问**: http://localhost (默认 80 端口)
## 表格组件开发规范
---
### 1. 表格行双击查看功能(强制性)
# Web 前端开发规范 (web/)
#### 实现要求
- **✅ 必须实现**: 所有数据表格必须支持行双击查看详情功能
- **🎯 目标**: 提升用户体验,快速查看数据详情
## 1. 技术栈规范
### 核心技术
- **框架**: Vue 3.5.13 + TypeScript 5.8.3
- **UI 组件库**: Element Plus 2.9.8
- **构建工具**: Vite 6.3.2
- **状态管理**: Pinia 3.0.2
- **路由管理**: Vue Router 4.5.0
- **HTTP 客户端**: Axios 1.8.4
- **代码风格**: ESLint 9.21.0 + Prettier 3.5.2
- **样式预处理**: SCSS (Sass 1.87.0)
- **原子化 CSS**: UnoCSS 66.0.0
- **Node.js**: 20.15.x+ (推荐 20.15.0)
- **npm**: 10.7.x+
### 环境要求
- **开发环境**: 支持本地环境或阿里云测试环境
- **数据库**: MySQL/Redis 支持本机安装或使用阿里云环境
- **前端访问**: http://localhost (默认 80 端口)
- **后端服务**: http://localhost:8080
## 🔴 前端模块管理规范(强制性,2025-01-23新增)
### 📦 前端模块文件夹规范
**⚠️ 基于后端模块工程统一规划,前端必须建立对应的模块文件夹结构!**
#### 🏗️ 模块命名和位置规范
**界面文件夹位置**: `web/src/views/{模块简称}/`
**API文件夹位置**: `web/src/api/{模块简称}/`
**已规划模块列表**:
- ✅ `hq` - 后勤管理模块 (已完成)
- ✅ `rs` - 人事管理模块
- ✅ `cw` - 财务管理模块
- ✅ `aqbw` - 安全保卫管理模块
- ✅ `zsjy` - 招生就业管理模块
- ✅ `xg` - 学工管理模块
#### 📁 标准目录结构
**界面文件夹结构** (web/src/views/{模块简称}/):
```
web/src/views/{模块简称}/
├── README.md # 模块说明文档
├── {功能模块1}/ # 按功能模块细分
│ ├── index.vue # 列表页面
│ ├── add.vue # 新增页面
│ ├── edit.vue # 编辑页面
│ └── detail.vue # 详情页面
├── {功能模块2}/
│ └── ...
└── components/ # 模块内公共组件
└── ...
```
**API文件夹结构** (web/src/api/{模块简称}/):
```
web/src/api/{模块简称}/
├── index.ts # API入口文件
├── README.md # API接口说明文档
├── types.ts # 类型定义文件
├── {功能模块1}/ # 按功能模块细分
│ └── index.ts # 功能模块API接口
├── {功能模块2}/
│ └── index.ts
└── ...
```
#### 🎯 文件命名规范
**Vue组件文件**:
- 页面组件: PascalCase (如: `UserList.vue`)
- 组件文件: kebab-case (如: `user-form.vue`)
**TypeScript文件**:
- API文件: kebab-case (如: `user-api.ts`)
- 类型文件: kebab-case (如: `user-types.ts`)
**路由命名**:
- 路由路径: kebab-case (如: `/rs/employee-list`)
- 路由名称: camelCase (如: `rsEmployeeList`)
#### 🔐 权限命名规范
**前端权限指令格式**: `v-hasPermi="'{模块简称}:{功能}:{操作}'"`
**示例**:
#### 标准实现方式
```vue
<!-- 人事模块员工新增权限 -->
<el-button v-hasPermi="'rs:employee:add'" type="primary">新增员工</el-button>
<!-- 表格组件 -->
<el-table
v-loading="loading"
:data="dataList"
@selection-change="handleSelectionChange"
@row-dblclick="handleView">
<!-- 表格列配置 -->
</el-table>
<!-- 财务模块预算查看权限 -->
<el-button v-hasPermi="'cw:budget:view'" type="primary">查看预算</el-button>
<!-- 学工模块学生编辑权限 -->
<el-button v-hasPermi="'xg:student:edit'" link type="primary">编辑</el-button>
<!-- 查看详情对话框 -->
<el-dialog
:title="viewDialog.title"
v-model="viewDialog.visible"
width="800px"
append-to-body>
<!-- 详情表单内容 -->
</el-dialog>
```
#### 📋 路由配置规范
**路由路径格式**: `/{模块简称}/{功能模块}`
**router配置示例**:
#### 标准事件处理
```typescript
// router/modules/rs.ts - 人事管理模块路由
export default {
path: '/rs',
name: 'Rs',
component: () => import('@/layout/index.vue'),
redirect: '/rs/employee',
meta: {
title: '人事管理',
icon: 'peoples',
rank: 3
},
children: [
{
path: '/rs/employee',
name: 'RsEmployee',
component: () => import('@/views/rs/employee/index.vue'),
meta: {
title: '员工管理',
keepAlive: true
}
},
{
path: '/rs/department',
name: 'RsDepartment',
component: () => import('@/views/rs/department/index.vue'),
meta: {
title: '部门管理',
keepAlive: true
}
}
]
/** 查看按钮操作 */
const handleView = async (row: any) => {
// 1. 复制数据到查看表单
Object.assign(viewForm, row);
// 2. 处理特殊字段(如图片列表等)
if (row.images) {
viewImageList.value = row.images.split(',').filter((img: string) => img.trim() !== '');
}
// 3. 显示对话框
viewDialog.visible = true;
viewDialog.title = '查看详情';
};
```
#### 🚨 前端模块开发检查清单
#### 最佳实践建议
1. 查看对话框宽度建议 800px-1000px,根据内容自适应
2. 详情展示采用只读表单形式,提供良好的数据展示结构
3. 对于图片、文件等特殊字段,需要合适的预览组件
4. 长文本内容建议使用滚动容器限制高度
5. 必要时可以在详情页面添加相关操作按钮
**创建新模块前必须检查**:
- [ ] 模块文件夹是否按规范命名?(`web/src/views/{简称}/`)
- [ ] API文件夹是否对应创建?(`web/src/api/{简称}/`)
- [ ] 是否创建了模块说明文档?(README.md)
- [ ] 路由配置是否符合规范?
- [ ] 权限指令是否使用正确的编码格式?
- [ ] 是否遵循前端开发规范和UI设计标准?
- [ ] 组件和文件命名是否符合规范?
- [ ] API接口类型定义是否与后端VO保持一致?
### 2. 表格长文本显示规范(强制性)
#### 💡 模块开发最佳实践
**1. 组件复用**:
- 模块内公共组件放在`components/`目录
- 跨模块公共组件放在`@/components/`目录
- 使用组合式API提高代码复用性
**2. 状态管理**:
- 模块相关状态使用Pinia store管理
- Store文件命名:`@/stores/{模块简称}.ts`
- 避免不同模块间的状态耦合
**3. 样式管理**:
- 使用SCSS变量和mixin提高样式复用
- 遵循Art Design Pro UI设计规范
- 统一使用轮廓式按钮设计
**4. 类型安全**:
- 所有API接口必须定义TypeScript类型
- 类型定义与后端VO对象保持一致
- 使用泛型提高类型复用性
#### 📝 注意事项
**模块间解耦**:
- 避免模块间直接依赖,通过事件总线或状态管理通信
- API接口按模块严格分离
- 路由按模块独立配置,避免交叉引用
**性能优化**:
- 页面组件使用懒加载
- 合理使用keep-alive缓存页面状态
- 图片资源优化和懒加载
**用户体验**:
- 统一的加载状态和错误处理
- 合理的页面过渡动画
- 响应式设计支持多设备访问
## 2. Art Design Pro UI 规范
### 设计理念
基于 Art Design Pro 的设计语言,追求现代化、精致、用户友好的界面体验。注重细节、动效和用户交互的流畅性。
### 颜色系统 🎨
#### 主色调 (Primary Colors) - 2025 年最新版本
```css
/* 亮色主题 */
:root {
--art-primary: 93, 135, 255; /* 主品牌色 - 亮蓝色 #5d87ff */
--art-secondary: 73, 190, 255; /* 辅助色 - 天蓝色 #49beff */
--art-success: 0, 167, 58; /* 成功色 - 绿色 #00a73a */
--art-warning: 142, 91, 0; /* 警告色 - 橙色 #8e5b00 */
--art-error: 250, 137, 107; /* 错误色 - 红色 #fa896b */
--art-info: 0, 75, 178; /* 信息色 - 蓝色 #004bb2 */
--art-danger: 255, 77, 79; /* 危险色 - 红色 #ff4d4f */
}
/* 暗色主题 */
html.dark {
--art-primary: 93, 135, 255; /* 主品牌色 - 亮蓝色 #5d87ff */
--art-secondary: 73, 190, 255; /* 辅助色 - 天蓝色 #49beff */
--art-success: 19, 222, 185; /* 成功色 - 绿色 #13deb9 */
--art-warning: 255, 174, 31; /* 警告色 - 橙色 #ffae1f */
--art-error: 250, 137, 107; /* 错误色 - 红色 #fa896b */
--art-info: 83, 155, 255; /* 信息色 - 蓝色 #539bff */
--art-danger: 255, 77, 79; /* 危险色 - 红色 #ff4d4f */
}
```
#### 背景色系统
```css
/* 浅色主题 */
:root {
--art-bg-color: #fafbfc; /* 页面底色 */
--art-main-bg-color: #ffffff; /* 主要内容背景 */
--art-bg-primary: 236, 242, 255; /* 主色背景 */
--art-bg-secondary: 232, 247, 255; /* 辅助背景 */
--art-bg-success: 230, 255, 250; /* 成功背景 */
--art-bg-warning: 254, 245, 229; /* 警告背景 */
--art-bg-error: 253, 237, 232; /* 错误背景 */
}
/* 暗色主题 */
html.dark {
--art-bg-color: #070707; /* 页面底色 */
--art-main-bg-color: #161618; /* 主要内容背景 */
}
```
#### 灰色系统
```css
:root {
--art-gray-100: #f9f9f9; /* 浅灰 */
--art-gray-200: #f1f1f4; /* 较浅灰 */
--art-gray-300: #dbdfe9; /* 中浅灰 */
--art-gray-400: #c4cada; /* 中灰 */
--art-gray-500: #99a1b7; /* 中深灰 */
--art-gray-600: #78829d; /* 深灰 */
--art-gray-700: #4b5675; /* 较深灰 */
--art-gray-800: #252f4a; /* 很深灰 */
--art-gray-900: #071437; /* 最深灰 */
}
```
### 轮廓式按钮设计系统 🔘
#### 🎨 设计理念
全面采用轮廓式(Outline)按钮设计,提供更加优雅、现代和一致的用户界面体验。
#### 核心规范
- **Primary 按钮**: 轮廓式主色设计,主色边框和文字,5%透明度主色背景
- **Success 按钮**: 轮廓式成功色设计,成功色边框和文字,5%透明度成功色背景
- **Warning 按钮**: 轮廓式警告色设计,警告色边框和文字,5%透明度警告色背景
- **Danger 按钮**: 轮廓式危险色设计,危险色边框和文字,5%透明度危险色背景
- **Info 按钮**: 轮廓式信息色设计,信息色边框和文字,5%透明度信息色背景
- **Default 按钮**: 白色背景,灰色边框,灰色文字
#### 交互效果
- **悬浮状态**: 上移 1px + 背景透明度变为 10% + 边框透明度变为 80% + 彩色阴影
- **点击状态**: 98%缩放 + 0.1s 快速过渡 + 背景透明度变为 15%
- **禁用状态**: 50%透明度,禁用所有交互和变换
### 🔘 列表页面按钮使用规范(2025 年最新标准)
#### 工具栏按钮规范
#### 实现要求
- **✅ 必须实现**: 所有表格中的长文本字段必须限制显示行数
- **🎯 目标**: 保持表格整洁美观,同时提供完整内容的查看方式
#### 标准实现方式
```vue
<!-- 标准工具栏按钮配置 -->
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<!-- ✅ 第一个新增按钮:必须使用 type="primary" -->
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<!-- ✅ 其他按钮:不添加type,使用默认样式 -->
<el-button icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Download" @click="handleExport">导出</el-button>
</el-col>
</el-row>
</template>
```
#### 操作列按钮规范
```vue
<!-- 表格操作列按钮配置 -->
<el-table-column label="操作" align="center" width="160">
<!-- 表格长文本列配置 -->
<el-table-column label="描述文本" align="center" prop="description">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<!-- ✅ 操作列按钮:统一使用 link type="primary" -->
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<div class="description-cell">{{ scope.row.description }}</div>
</template>
</el-table-column>
```
#### 按钮使用规范总结
| 位置 | 按钮类型 | 样式规范 | 示例 |
| -------------------- | ---------------- | ---------------------- | -------------------------------------------------------------- |
| 工具栏第一个新增按钮 | 新增 | `type="primary" plain` | `<el-button type="primary" plain icon="Plus">新增</el-button>` |
| 工具栏其他按钮 | 修改/删除/导出等 | 无 type(默认) | `<el-button icon="Edit">修改</el-button>` |
| 表格操作列 | 所有操作按钮 | `link type="primary"` | `<el-button link type="primary" icon="Edit"></el-button>` |
| 弹窗底部 | 确认按钮 | `type="primary"` | `<el-button type="primary">确定</el-button>` |
| 弹窗底部 | 取消按钮 | 无 type(默认) | `<el-button>取消</el-button>` |
### 圆角系统
#### 标准样式定义
```css
:root {
--border-radius-xs: 4px; /* 超小元素 (checkbox等) */
--border-radius-sm: 6px; /* 小元素 (下拉选项、关闭按钮等) */
--border-radius-md: 8px; /* 表单控件 (input、select、button等) */
--border-radius-lg: 12px; /* 容器组件 (card、table、dialog等) */
--border-radius-xl: 12px;
--border-radius-2xl: 16px;
--border-radius-full: 50%;
/* 长文本单元格样式 */
.description-cell {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; /* 显示3行 */
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.5;
max-height: 4.5em; /* 3行 * 1.5行高 */
text-align: left;
padding: 0 10px;
}
```
### 间距系统
#### 最佳实践建议
1. 统一使用3行显示限制,超出显示省略号
2. 使用左对齐方式,提高长文本可读性
3. 设置合适的行高和内边距
4. 配合表格行双击查看功能,在详情弹窗中显示完整内容
5. 必要时可以添加鼠标悬停提示(el-tooltip)显示完整内容
```css
:root {
--space-xs: 4px;
--space-sm: 8px;
--space-md: 12px;
--space-lg: 16px;
--space-xl: 20px;
--space-2xl: 24px;
--space-3xl: 32px;
--space-4xl: 48px;
--space-5xl: 64px;
}
```
## 3. 组件开发规范
### Vue 组件结构标准
### 3. 组件开发标准和页面模板
```vue
<template>
@@ -425,8 +144,6 @@ onMounted(() => {
</style>
```
### 页面开发规范
#### 列表页面标准模板
```vue
@@ -604,9 +321,7 @@ const resetForm = () => {
</script>
```
## 4. API 开发规范
### API 接口定义标准
### 3. API接口定义和状态管理
```typescript
// api/[module]/index.ts
@@ -816,7 +531,89 @@ const moduleRouter: RouteRecordRaw = {
export default moduleRouter;
```
## 7. 项目架构规范
## 7. 搜索表单布局规范(强制性)
### 7.1 设计目标
1. **一致性**:所有列表页面的检索栏保持统一外观和交互。
2. **响应式**:在 1920/1440/1280 等常用分辨率下均能保持整洁布局。
3. **易维护**:通过统一的栅格 + 样式类,后续页面可快速复用。
### 7.2 栅格布局
1. 采用 Element Plus `el-row` / `el-col` 栅格系统。
2. **每行固定 4 个检索元素**,每项 `:span="6"`(24 ÷ 4)。
3. 若检索项不足 4 个,也应保持占位对齐,避免按钮漂移。
```vue
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="字段1" prop="field1">
<el-input v-model="queryParams.field1" placeholder="请输入字段1" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="字段2" prop="field2">
<el-input v-model="queryParams.field2" placeholder="请输入字段2" clearable />
</el-form-item>
</el-col>
<!-- 其余两列同理 -->
</el-row>
<!-- 更多行按需添加,仍保持 4 列规则 -->
<!-- 操作按钮行 -->
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
```
### 7.3 按钮固定右侧
1. 统一在搜索表单尾部添加按钮行。
2. 使用 `.search-btn-container` 类,将按钮通过 Flex **右对齐**。
```scss
/* 搜索按钮容器样式(全局复用,可写在组件 scoped 内部)*/
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important; // 清除默认 margin
}
}
```
### 7.4 应用示例
所有新页面或改造页面 **必须** 遵循以上规范。例如:
* `hq/pm/repairOrder/index.vue`
* `hq/pm/repairCategory/index.vue`
* `hq/hallAudit/index.vue`
* `hq/hallInfo/index.vue`
* `hq/hallReservation/index.vue`
### 7.5 代码检查要点
1. 检查是否存在 `:inline="true"`,如有应移除。
2. 确认每行 4 列,使用 `span=6`。
3. 搜索/重置按钮是否放置在 `.search-btn-container` 内。
4. scoped 样式内是否包含上述 `.search-btn-container` 定义。
遵循此规范,可显著提升列表页检索区域的统一性与用户体验。
## 8. 项目架构规范
### 目录结构
@@ -853,7 +650,7 @@ web/src/
- **工具文件**: camelCase,如 `formatUtils.ts`
- **类型文件**: PascalCase,如 `UserTypes.ts`
## 8. 样式开发规范
## 9. 样式开发规范
### SCSS 变量使用
@@ -890,7 +687,7 @@ web/src/
</template>
```
## 9. 性能优化规范
## 10. 性能优化规范
### 组件懒加载
@@ -911,7 +708,7 @@ const AsyncComponent = defineAsyncComponent(() => import('@/components/HeavyComp
</template>
```
## 10. 开发最佳实践
## 11. 开发最佳实践
### 代码规范
@@ -952,7 +749,7 @@ interface UserForm {
const user = data as UserForm;
```
## 11. 测试规范
## 12. 测试规范
### 单元测试
@@ -970,7 +767,7 @@ describe('UserForm', () => {
});
```
## 12. 构建与部署
## 13. 构建与部署
### 环境配置
@@ -20,6 +20,8 @@ import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.hq.domain.vo.HqHallReservationVo;
import org.dromara.hq.domain.bo.HqHallReservationBo;
import org.dromara.hq.domain.bo.HqHallReservationAuditBo;
import org.dromara.hq.domain.bo.HqHallTimeCheckBo;
import org.dromara.hq.domain.vo.HqHallTimeCheckVo;
import org.dromara.hq.service.IHqHallReservationService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -112,4 +114,14 @@ public class HqHallReservationController extends BaseController {
public R<Void> audit(@RequestBody HqHallReservationAuditBo auditBo) {
return toAjax(hqHallReservationService.auditByIds(auditBo));
}
/**
* 检查报告厅时间冲突
*/
@SaCheckPermission("hq:hallReservation:query")
@PostMapping("/checkTimeConflict")
public R<HqHallTimeCheckVo> checkTimeConflict(@Validated @RequestBody HqHallTimeCheckBo timeCheckBo) {
HqHallTimeCheckVo result = hqHallReservationService.checkTimeConflict(timeCheckBo);
return R.ok(result);
}
}
@@ -15,9 +15,9 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.hq.domain.bo.PmRepairAttachmentBo;
import org.dromara.hq.domain.vo.PmRepairAttachmentVo;
import org.dromara.hq.service.IPmRepairAttachmentService;
import org.dromara.hq.domain.bo.HqPmRepairAttachmentBo;
import org.dromara.hq.domain.vo.HqPmRepairAttachmentVo;
import org.dromara.hq.service.IHqPmRepairAttachmentService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -35,16 +35,16 @@ import java.util.List;
@RequiredArgsConstructor
@RestController
@RequestMapping("/hq/pm/attachment")
public class PmRepairAttachmentController extends BaseController {
public class HqPmRepairAttachmentController extends BaseController {
private final IPmRepairAttachmentService attachmentService;
private final IHqPmRepairAttachmentService attachmentService;
/**
* 查询报修附件列表
*/
// @SaCheckPermission("pm:attachment:list")
@GetMapping("/list")
public TableDataInfo<PmRepairAttachmentVo> list(PmRepairAttachmentBo bo, PageQuery pageQuery) {
public TableDataInfo<HqPmRepairAttachmentVo> list(HqPmRepairAttachmentBo bo, PageQuery pageQuery) {
return attachmentService.queryPageList(bo, pageQuery);
}
@@ -53,8 +53,8 @@ public class PmRepairAttachmentController extends BaseController {
*/
// @SaCheckPermission("pm:attachment:list")
@GetMapping("/order/{orderId}")
public R<List<PmRepairAttachmentVo>> getByOrderId(@PathVariable("orderId") Long orderId) {
List<PmRepairAttachmentVo> list = attachmentService.queryByOrderId(orderId);
public R<List<HqPmRepairAttachmentVo>> getByOrderId(@PathVariable("orderId") Long orderId) {
List<HqPmRepairAttachmentVo> list = attachmentService.queryByOrderId(orderId);
return R.ok(list);
}
@@ -63,7 +63,7 @@ public class PmRepairAttachmentController extends BaseController {
*/
// @SaCheckPermission("pm:attachment:query")
@GetMapping("/{id}")
public R<PmRepairAttachmentVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
public R<HqPmRepairAttachmentVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(attachmentService.queryById(id));
}
@@ -74,7 +74,7 @@ public class PmRepairAttachmentController extends BaseController {
@Log(title = "报修附件", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping
public R<Void> add(@Validated(AddGroup.class) @RequestBody PmRepairAttachmentBo bo) {
public R<Void> add(@Validated(AddGroup.class) @RequestBody HqPmRepairAttachmentBo bo) {
return toAjax(attachmentService.insertByBo(bo));
}
@@ -85,7 +85,7 @@ public class PmRepairAttachmentController extends BaseController {
@Log(title = "报修附件", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping
public R<Void> edit(@Validated(EditGroup.class) @RequestBody PmRepairAttachmentBo bo) {
public R<Void> edit(@Validated(EditGroup.class) @RequestBody HqPmRepairAttachmentBo bo) {
return toAjax(attachmentService.updateByBo(bo));
}
@@ -106,7 +106,7 @@ public class PmRepairAttachmentController extends BaseController {
// @SaCheckPermission("pm:attachment:upload")
@Log(title = "上传报修附件", businessType = BusinessType.INSERT)
@PostMapping("/upload")
public R<PmRepairAttachmentVo> uploadFile(
public R<HqPmRepairAttachmentVo> uploadFile(
@RequestParam("orderId") Long orderId,
@RequestParam(value = "uploadStage", defaultValue = "1") String uploadStage,
@RequestParam("file") MultipartFile file,
@@ -117,7 +117,7 @@ public class PmRepairAttachmentController extends BaseController {
}
try {
PmRepairAttachmentVo result = attachmentService.uploadFile(orderId, uploadStage, file, description);
HqPmRepairAttachmentVo result = attachmentService.uploadFile(orderId, uploadStage, file, description);
return R.ok(result);
} catch (Exception e) {
return R.fail("上传失败: " + e.getMessage());
@@ -130,7 +130,7 @@ public class PmRepairAttachmentController extends BaseController {
// @SaCheckPermission("pm:attachment:upload")
@Log(title = "批量上传报修附件", businessType = BusinessType.INSERT)
@PostMapping("/upload/batch")
public R<List<PmRepairAttachmentVo>> uploadFiles(
public R<List<HqPmRepairAttachmentVo>> uploadFiles(
@RequestParam("orderId") Long orderId,
@RequestParam(value = "uploadStage", defaultValue = "1") String uploadStage,
@RequestParam("files") MultipartFile[] files,
@@ -141,7 +141,7 @@ public class PmRepairAttachmentController extends BaseController {
}
try {
List<PmRepairAttachmentVo> results = attachmentService.uploadFiles(orderId, uploadStage, files,
List<HqPmRepairAttachmentVo> results = attachmentService.uploadFiles(orderId, uploadStage, files,
descriptions);
return R.ok(results);
} catch (Exception e) {
@@ -165,7 +165,7 @@ public class PmRepairAttachmentController extends BaseController {
// @SaCheckPermission("pm:attachment:download")
@GetMapping("/download/{id}")
public void downloadFile(@PathVariable("id") Long id, HttpServletResponse response) {
PmRepairAttachmentVo attachment = attachmentService.queryById(id);
HqPmRepairAttachmentVo attachment = attachmentService.queryById(id);
if (attachment == null) {
return;
}
@@ -17,9 +17,9 @@ import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.hq.domain.vo.PmRepairCategoryVo;
import org.dromara.hq.domain.bo.PmRepairCategoryBo;
import org.dromara.hq.service.IPmRepairCategoryService;
import org.dromara.hq.domain.vo.HqPmRepairCategoryVo;
import org.dromara.hq.domain.bo.HqPmRepairCategoryBo;
import org.dromara.hq.service.IHqPmRepairCategoryService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
@@ -32,16 +32,16 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
@RequiredArgsConstructor
@RestController
@RequestMapping("/hq/pm/repairCategory")
public class PmRepairCategoryController extends BaseController {
public class HqPmRepairCategoryController extends BaseController {
private final IPmRepairCategoryService repairCategoryService;
private final IHqPmRepairCategoryService repairCategoryService;
/**
* 查询维修类型列表
*/
@SaCheckPermission("pm:category:list")
@GetMapping("/list")
public TableDataInfo<PmRepairCategoryVo> list(PmRepairCategoryBo bo, PageQuery pageQuery) {
public TableDataInfo<HqPmRepairCategoryVo> list(HqPmRepairCategoryBo bo, PageQuery pageQuery) {
return repairCategoryService.queryPageList(bo, pageQuery);
}
@@ -51,9 +51,9 @@ public class PmRepairCategoryController extends BaseController {
@SaCheckPermission("pm:category:export")
@Log(title = "维修类型", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(PmRepairCategoryBo bo, HttpServletResponse response) {
List<PmRepairCategoryVo> list = repairCategoryService.queryList(bo);
ExcelUtil.exportExcel(list, "维修类型", PmRepairCategoryVo.class, response);
public void export(HqPmRepairCategoryBo bo, HttpServletResponse response) {
List<HqPmRepairCategoryVo> list = repairCategoryService.queryList(bo);
ExcelUtil.exportExcel(list, "维修类型", HqPmRepairCategoryVo.class, response);
}
/**
@@ -61,7 +61,7 @@ public class PmRepairCategoryController extends BaseController {
*/
@SaCheckPermission("pm:category:query")
@GetMapping("/{id}")
public R<PmRepairCategoryVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
public R<HqPmRepairCategoryVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(repairCategoryService.queryById(id));
}
@@ -72,7 +72,7 @@ public class PmRepairCategoryController extends BaseController {
@Log(title = "维修类型", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody PmRepairCategoryBo bo) {
public R<Void> add(@Validated(AddGroup.class) @RequestBody HqPmRepairCategoryBo bo) {
return toAjax(repairCategoryService.insertByBo(bo));
}
@@ -83,7 +83,7 @@ public class PmRepairCategoryController extends BaseController {
@Log(title = "维修类型", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody PmRepairCategoryBo bo) {
public R<Void> edit(@Validated(EditGroup.class) @RequestBody HqPmRepairCategoryBo bo) {
return toAjax(repairCategoryService.updateByBo(bo));
}
@@ -102,7 +102,7 @@ public class PmRepairCategoryController extends BaseController {
*/
@SaCheckPermission("pm:category:tree")
@GetMapping("/tree")
public R<List<PmRepairCategoryVo>> tree() {
public R<List<HqPmRepairCategoryVo>> tree() {
return R.ok(repairCategoryService.queryTree());
}
@@ -112,7 +112,7 @@ public class PmRepairCategoryController extends BaseController {
@SaCheckPermission("pm:category:options")
@GetMapping("/options")
public R<List<java.util.Map<String, Object>>> options() {
List<PmRepairCategoryVo> list = repairCategoryService.queryTree();
List<HqPmRepairCategoryVo> list = repairCategoryService.queryTree();
List<java.util.Map<String, Object>> options = buildCategoryOptions(list);
return R.ok(options);
}
@@ -120,9 +120,9 @@ public class PmRepairCategoryController extends BaseController {
/**
* 构建维修类型下拉选项
*/
private List<java.util.Map<String, Object>> buildCategoryOptions(List<PmRepairCategoryVo> list) {
private List<java.util.Map<String, Object>> buildCategoryOptions(List<HqPmRepairCategoryVo> list) {
List<java.util.Map<String, Object>> result = new java.util.ArrayList<>();
for (PmRepairCategoryVo item : list) {
for (HqPmRepairCategoryVo item : list) {
java.util.Map<String, Object> option = new java.util.HashMap<>();
option.put("value", item.getId());
option.put("label", item.getCategoryName());
@@ -17,11 +17,11 @@ import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.hq.domain.vo.PmRepairOrderVo;
import org.dromara.hq.domain.bo.PmRepairOrderBo;
import org.dromara.hq.domain.vo.PmRepairAttachmentVo;
import org.dromara.hq.service.IPmRepairOrderService;
import org.dromara.hq.service.IPmRepairAttachmentService;
import org.dromara.hq.domain.vo.HqPmRepairOrderVo;
import org.dromara.hq.domain.bo.HqPmRepairOrderBo;
import org.dromara.hq.domain.vo.HqPmRepairAttachmentVo;
import org.dromara.hq.service.IHqPmRepairOrderService;
import org.dromara.hq.service.IHqPmRepairAttachmentService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
@@ -34,18 +34,18 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
@RequiredArgsConstructor
@RestController
@RequestMapping("/hq/pm/repairOrder")
public class PmRepairOrderController extends BaseController {
public class HqPmRepairOrderController extends BaseController {
private final IPmRepairOrderService repairOrderService;
private final IPmRepairAttachmentService attachmentService;
private final IHqPmRepairOrderService repairOrderService;
private final IHqPmRepairAttachmentService attachmentService;
/**
* 查询报修工单列表
*/
@SaCheckPermission("pm:repairOrder:list")
@GetMapping("/list")
public TableDataInfo<PmRepairOrderVo> list(PmRepairOrderBo bo, PageQuery pageQuery) {
TableDataInfo<PmRepairOrderVo> result = repairOrderService.queryPageList(bo, pageQuery);
public TableDataInfo<HqPmRepairOrderVo> list(HqPmRepairOrderBo bo, PageQuery pageQuery) {
TableDataInfo<HqPmRepairOrderVo> result = repairOrderService.queryPageList(bo, pageQuery);
return result;
}
@@ -55,9 +55,9 @@ public class PmRepairOrderController extends BaseController {
@SaCheckPermission("pm:repairOrder:export")
@Log(title = "报修工单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(PmRepairOrderBo bo, HttpServletResponse response) {
List<PmRepairOrderVo> list = repairOrderService.queryList(bo);
ExcelUtil.exportExcel(list, "报修工单", PmRepairOrderVo.class, response);
public void export(HqPmRepairOrderBo bo, HttpServletResponse response) {
List<HqPmRepairOrderVo> list = repairOrderService.queryList(bo);
ExcelUtil.exportExcel(list, "报修工单", HqPmRepairOrderVo.class, response);
}
/**
@@ -65,7 +65,7 @@ public class PmRepairOrderController extends BaseController {
*/
@SaCheckPermission("pm:repairOrder:query")
@GetMapping("/{id}")
public R<PmRepairOrderVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
public R<HqPmRepairOrderVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(repairOrderService.queryById(id));
}
@@ -76,7 +76,7 @@ public class PmRepairOrderController extends BaseController {
@Log(title = "报修工单", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Long> add(@Validated(AddGroup.class) @RequestBody PmRepairOrderBo bo) {
public R<Long> add(@Validated(AddGroup.class) @RequestBody HqPmRepairOrderBo bo) {
boolean success = repairOrderService.insertByBo(bo);
if (success) {
// 返回创建的工单ID,供前端上传附件使用
@@ -93,7 +93,7 @@ public class PmRepairOrderController extends BaseController {
@Log(title = "报修工单", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody PmRepairOrderBo bo) {
public R<Void> edit(@Validated(EditGroup.class) @RequestBody HqPmRepairOrderBo bo) {
return toAjax(repairOrderService.updateByBo(bo));
}
@@ -323,9 +323,9 @@ public class PmRepairOrderController extends BaseController {
*/
@SaCheckPermission("pm:repairOrder:query")
@GetMapping("/{orderId}/attachments")
public R<List<PmRepairAttachmentVo>> getRepairAttachments(
public R<List<HqPmRepairAttachmentVo>> getRepairAttachments(
@NotNull(message = "工单ID不能为空") @PathVariable Long orderId) {
List<PmRepairAttachmentVo> attachments = attachmentService.queryByOrderId(orderId);
List<HqPmRepairAttachmentVo> attachments = attachmentService.queryByOrderId(orderId);
return R.ok(attachments);
}
@@ -10,15 +10,15 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 报修附件对象 pm_repair_attachment
* 报修附件对象 hq_pm_repair_attachment
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("pm_repair_attachment")
public class PmRepairAttachment extends TenantEntity {
@TableName("hq_pm_repair_attachment")
public class HqPmRepairAttachment extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
@@ -30,7 +30,7 @@ public class PmRepairAttachment extends TenantEntity {
private Long id;
/**
* 报修工单ID,关联pm_repair_order.id
* 报修工单ID,关联hq_pm_repair_order.id
*/
private Long orderId;
@@ -11,15 +11,15 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 维修类型对象 pm_repair_category
* 维修类型对象 hq_pm_repair_category
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("pm_repair_category")
public class PmRepairCategory extends TenantEntity {
@TableName("hq_pm_repair_category")
public class HqPmRepairCategory extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
@@ -11,15 +11,15 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 报修工单对象 pm_repair_order
* 报修工单对象 hq_pm_repair_order
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("pm_repair_order")
public class PmRepairOrder extends TenantEntity {
@TableName("hq_pm_repair_order")
public class HqPmRepairOrder extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
@@ -51,7 +51,7 @@ public class PmRepairOrder extends TenantEntity {
private String repairLocation;
/**
* 维修类型ID,关联pm_repair_category.id
* 维修类型ID,关联hq_pm_repair_category.id
*/
private Long categoryId;
@@ -18,8 +18,8 @@ import java.io.Serial;
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("pm_repair_progress")
public class PmRepairProgress extends TenantEntity {
@TableName("hq_pm_repair_progress")
public class HqPmRepairProgress extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
@@ -31,7 +31,7 @@ public class PmRepairProgress extends TenantEntity {
private Long id;
/**
* 报修工单ID,关联pm_repair_order.id
* 报修工单ID,关联hq_pm_repair_order.id
*/
private Long orderId;
@@ -11,15 +11,15 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 服务评价对象 pm_service_evaluation
* 服务评价对象 hq_pm_service_evaluation
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("pm_service_evaluation")
public class PmServiceEvaluation extends TenantEntity {
@TableName("hq_pm_service_evaluation")
public class HqPmServiceEvaluation extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
@@ -31,7 +31,7 @@ public class PmServiceEvaluation extends TenantEntity {
private Long id;
/**
* 报修工单ID,关联pm_repair_order.id
* 报修工单ID,关联hq_pm_repair_order.id
*/
private Long orderId;
@@ -0,0 +1,41 @@
package org.dromara.hq.domain.bo;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 报告厅时间冲突检查业务对象
*
* @author wuzilong
* @date 2025-01-09
*/
@Data
public class HqHallTimeCheckBo {
/**
* 报告厅ID
*/
@NotNull(message = "报告厅ID不能为空")
private Long hallId;
/**
* 开始时间
*/
@NotNull(message = "开始时间不能为空")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/**
* 结束时间
*/
@NotNull(message = "结束时间不能为空")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
/**
* 排除的预约ID(编辑时排除自己)
*/
private Long excludeId;
}
@@ -9,20 +9,20 @@ import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.hq.domain.PmRepairAttachment;
import org.dromara.hq.domain.HqPmRepairAttachment;
import java.util.Date;
/**
* 报修附件业务对象 pm_repair_attachment
* 报修附件业务对象 hq_pm_repair_attachment
*
* @author Cursor AI
* @date 2025-01-26
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = PmRepairAttachment.class, reverseConvertGenerate = false)
public class PmRepairAttachmentBo extends BaseEntity {
@AutoMapper(target = HqPmRepairAttachment.class, reverseConvertGenerate = false)
public class HqPmRepairAttachmentBo extends BaseEntity {
/**
* 主键ID
@@ -1,6 +1,6 @@
package org.dromara.hq.domain.bo;
import org.dromara.hq.domain.PmRepairCategory;
import org.dromara.hq.domain.HqPmRepairCategory;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
@@ -14,15 +14,15 @@ import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 维修类型业务对象 pm_repair_category
* 维修类型业务对象 hq_pm_repair_category
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = PmRepairCategory.class, reverseConvertGenerate = false)
public class PmRepairCategoryBo extends BaseEntity {
@AutoMapper(target = HqPmRepairCategory.class, reverseConvertGenerate = false)
public class HqPmRepairCategoryBo extends BaseEntity {
/**
* 主键ID
@@ -1,6 +1,6 @@
package org.dromara.hq.domain.bo;
import org.dromara.hq.domain.PmRepairOrder;
import org.dromara.hq.domain.HqPmRepairOrder;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
@@ -14,15 +14,15 @@ import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 报修工单业务对象 pm_repair_order
* 报修工单业务对象 hq_pm_repair_order
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = PmRepairOrder.class, reverseConvertGenerate = false)
public class PmRepairOrderBo extends BaseEntity {
@AutoMapper(target = HqPmRepairOrder.class, reverseConvertGenerate = false)
public class HqPmRepairOrderBo extends BaseEntity {
/**
* 主键ID
@@ -0,0 +1,124 @@
package org.dromara.hq.domain.bo;
import org.dromara.hq.domain.HqPmRepairProgress;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.math.BigDecimal;
/**
* 维修进度记录业务对象 hq_pm_repair_progress
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = HqPmRepairProgress.class, reverseConvertGenerate = false)
public class HqPmRepairProgressBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 报修工单ID,关联hq_pm_repair_order.id
*/
@NotNull(message = "报修工单ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long orderId;
/**
* 进度类型:1-工单分派,2-接收确认,3-到达现场,4-开始维修,5-进度更新,6-完成维修,7-用户评价,8-工单归档,9-其他
*/
@NotBlank(message = "进度类型不能为空", groups = { AddGroup.class, EditGroup.class })
private String progressType;
/**
* 进度状态描述
*/
@NotBlank(message = "进度状态描述不能为空", groups = { AddGroup.class, EditGroup.class })
private String progressStatus;
/**
* 进度详细描述
*/
private String progressDetail;
/**
* 操作人ID,关联sys_user.id
*/
@NotNull(message = "操作人ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long operatorId;
/**
* 操作人姓名
*/
@NotBlank(message = "操作人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String operatorName;
/**
* 操作人角色:user-用户,admin-管理员,worker-维修员
*/
@NotBlank(message = "操作人角色不能为空", groups = { AddGroup.class, EditGroup.class })
private String operatorRole;
/**
* 操作位置信息
*/
private String locationInfo;
/**
* GPS纬度
*/
private BigDecimal gpsLatitude;
/**
* GPS经度
*/
private BigDecimal gpsLongitude;
/**
* 操作时间
*/
@NotNull(message = "操作时间不能为空", groups = { AddGroup.class, EditGroup.class })
private Date operationTime;
/**
* 预计耗时(分钟)
*/
private Integer estimatedDuration;
/**
* 关联附件ID列表(逗号分隔)
*/
private String attachmentIds;
/**
* 下一步计划
*/
private String nextStep;
/**
* 遇到的问题
*/
private String issuesEncountered;
/**
* 需要的材料
*/
private String materialsNeeded;
/**
* 备注信息
*/
private String remark;
}
@@ -0,0 +1,160 @@
package org.dromara.hq.domain.bo;
import org.dromara.hq.domain.HqPmServiceEvaluation;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.math.BigDecimal;
/**
* 服务评价业务对象 hq_pm_service_evaluation
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = HqPmServiceEvaluation.class, reverseConvertGenerate = false)
public class HqPmServiceEvaluationBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 报修工单ID,关联hq_pm_repair_order.id
*/
@NotNull(message = "报修工单ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long orderId;
/**
* 评价人ID,关联sys_user.id
*/
@NotNull(message = "评价人ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long evaluatorId;
/**
* 评价人姓名
*/
@NotBlank(message = "评价人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String evaluatorName;
/**
* 被评价维修员ID,关联sys_user.id
*/
@NotNull(message = "被评价维修员ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long evaluatedWorkerId;
/**
* 被评价维修员姓名
*/
@NotBlank(message = "被评价维修员姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String evaluatedWorkerName;
/**
* 综合评价得分(1.0-5.0)
*/
@NotNull(message = "综合评价得分不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "1.0", message = "综合评价得分不能小于1.0")
@DecimalMax(value = "5.0", message = "综合评价得分不能大于5.0")
private BigDecimal overallScore;
/**
* 维修质量得分(1.0-5.0)
*/
@DecimalMin(value = "1.0", message = "维修质量得分不能小于1.0")
@DecimalMax(value = "5.0", message = "维修质量得分不能大于5.0")
private BigDecimal qualityScore;
/**
* 服务态度得分(1.0-5.0)
*/
@DecimalMin(value = "1.0", message = "服务态度得分不能小于1.0")
@DecimalMax(value = "5.0", message = "服务态度得分不能大于5.0")
private BigDecimal attitudeScore;
/**
* 响应速度得分(1.0-5.0)
*/
@DecimalMin(value = "1.0", message = "响应速度得分不能小于1.0")
@DecimalMax(value = "5.0", message = "响应速度得分不能大于5.0")
private BigDecimal responseSpeedScore;
/**
* 沟通效果得分(1.0-5.0)
*/
@DecimalMin(value = "1.0", message = "沟通效果得分不能小于1.0")
@DecimalMax(value = "5.0", message = "沟通效果得分不能大于5.0")
private BigDecimal communicationScore;
/**
* 文字评价内容
*/
private String evaluationContent;
/**
* 满意度等级:1-非常满意,2-满意,3-一般,4-不满意,5-非常不满意
*/
@NotBlank(message = "满意度等级不能为空", groups = { AddGroup.class, EditGroup.class })
private String satisfactionLevel;
/**
* 是否匿名评价(0-实名,1-匿名)
*/
private Integer isAnonymous;
/**
* 是否公开显示(0-不公开,1-公开)
*/
private Integer isPublic;
/**
* 评价时间
*/
@NotNull(message = "评价时间不能为空", groups = { AddGroup.class, EditGroup.class })
private Date evaluationTime;
/**
* 服务开始时间
*/
private Date serviceStartTime;
/**
* 服务结束时间
*/
private Date serviceEndTime;
/**
* 服务时长(分钟)
*/
private Integer serviceDuration;
/**
* 正面标签(逗号分隔)
*/
private String positiveTags;
/**
* 负面标签(逗号分隔)
*/
private String negativeTags;
/**
* 改进建议
*/
private String improvementSuggestions;
/**
* 备注信息
*/
private String remark;
}
@@ -113,4 +113,29 @@ public class HqHallReservationVo implements Serializable {
@ExcelProperty(value = "审核备注")
private String auditRemark;
// ========== 基础字段(强制性,所有VO类必须包含) ==========
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,57 @@
package org.dromara.hq.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 报告厅时间冲突检查结果视图对象
*
* @author wuzilong
* @date 2025-01-09
*/
@Data
public class HqHallTimeCheckVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 是否存在时间冲突
*/
private Boolean hasConflict;
/**
* 冲突的预约记录列表
*/
private List<HqHallReservationVo> conflictList;
/**
* 冲突描述信息
*/
private String conflictMessage;
public HqHallTimeCheckVo() {
}
public HqHallTimeCheckVo(Boolean hasConflict, List<HqHallReservationVo> conflictList, String conflictMessage) {
this.hasConflict = hasConflict;
this.conflictList = conflictList;
this.conflictMessage = conflictMessage;
}
/**
* 创建无冲突结果
*/
public static HqHallTimeCheckVo noConflict() {
return new HqHallTimeCheckVo(false, List.of(), "无时间冲突");
}
/**
* 创建有冲突结果
*/
public static HqHallTimeCheckVo hasConflict(List<HqHallReservationVo> conflictList, String message) {
return new HqHallTimeCheckVo(true, conflictList, message);
}
}
@@ -5,22 +5,22 @@ import cn.idev.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.hq.domain.PmRepairAttachment;
import org.dromara.hq.domain.HqPmRepairAttachment;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 报修附件视图对象 pm_repair_attachment
* 报修附件视图对象 hq_pm_repair_attachment
*
* @author Cursor AI
* @date 2025-01-26
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = PmRepairAttachment.class)
public class PmRepairAttachmentVo implements Serializable {
@AutoMapper(target = HqPmRepairAttachment.class)
public class HqPmRepairAttachmentVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -4,7 +4,7 @@ import java.util.Date;
import java.math.BigDecimal;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.hq.domain.PmRepairCategory;
import org.dromara.hq.domain.HqPmRepairCategory;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
@@ -16,15 +16,15 @@ import java.io.Serial;
import java.io.Serializable;
/**
* 维修类型视图对象 pm_repair_category
* 维修类型视图对象 hq_pm_repair_category
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = PmRepairCategory.class)
public class PmRepairCategoryVo implements Serializable {
@AutoMapper(target = HqPmRepairCategory.class)
public class HqPmRepairCategoryVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -156,6 +156,6 @@ public class PmRepairCategoryVo implements Serializable {
/**
* 子分类列表(用于树形结构)
*/
private List<PmRepairCategoryVo> children;
private List<HqPmRepairCategoryVo> children;
}
@@ -3,7 +3,7 @@ package org.dromara.hq.domain.vo;
import java.util.Date;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.hq.domain.PmRepairOrder;
import org.dromara.hq.domain.HqPmRepairOrder;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
@@ -15,15 +15,15 @@ import java.io.Serial;
import java.io.Serializable;
/**
* 报修工单视图对象 pm_repair_order
* 报修工单视图对象 hq_pm_repair_order
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = PmRepairOrder.class)
public class PmRepairOrderVo implements Serializable {
@AutoMapper(target = HqPmRepairOrder.class)
public class HqPmRepairOrderVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -0,0 +1,171 @@
package org.dromara.hq.domain.vo;
import java.util.Date;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.hq.domain.HqPmRepairProgress;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 维修进度记录视图对象 hq_pm_repair_progress
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = HqPmRepairProgress.class)
public class HqPmRepairProgressVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 报修工单ID,关联hq_pm_repair_order.id
*/
@ExcelProperty(value = "报修工单ID")
private Long orderId;
/**
* 进度类型:1-工单分派,2-接收确认,3-到达现场,4-开始维修,5-进度更新,6-完成维修,7-用户评价,8-工单归档,9-其他
*/
@ExcelProperty(value = "进度类型", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "pm_progress_type")
private String progressType;
/**
* 进度状态描述
*/
@ExcelProperty(value = "进度状态描述")
private String progressStatus;
/**
* 进度详细描述
*/
@ExcelProperty(value = "进度详细描述")
private String progressDetail;
/**
* 操作人ID,关联sys_user.id
*/
@ExcelProperty(value = "操作人ID")
private Long operatorId;
/**
* 操作人姓名
*/
@ExcelProperty(value = "操作人姓名")
private String operatorName;
/**
* 操作人角色:user-用户,admin-管理员,worker-维修员
*/
@ExcelProperty(value = "操作人角色", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "pm_operator_role")
private String operatorRole;
/**
* 操作位置信息
*/
@ExcelProperty(value = "操作位置信息")
private String locationInfo;
/**
* GPS纬度
*/
@ExcelProperty(value = "GPS纬度")
private BigDecimal gpsLatitude;
/**
* GPS经度
*/
@ExcelProperty(value = "GPS经度")
private BigDecimal gpsLongitude;
/**
* 操作时间
*/
@ExcelProperty(value = "操作时间")
private Date operationTime;
/**
* 预计耗时(分钟)
*/
@ExcelProperty(value = "预计耗时(分钟)")
private Integer estimatedDuration;
/**
* 关联附件ID列表(逗号分隔)
*/
@ExcelProperty(value = "关联附件ID列表")
private String attachmentIds;
/**
* 下一步计划
*/
@ExcelProperty(value = "下一步计划")
private String nextStep;
/**
* 遇到的问题
*/
@ExcelProperty(value = "遇到的问题")
private String issuesEncountered;
/**
* 需要的材料
*/
@ExcelProperty(value = "需要的材料")
private String materialsNeeded;
/**
* 备注信息
*/
@ExcelProperty(value = "备注信息")
private String remark;
/**
* 租户ID
*/
private String tenantId;
/**
* 创建部门
*/
private Long createDept;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,207 @@
package org.dromara.hq.domain.vo;
import java.util.Date;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.hq.domain.HqPmServiceEvaluation;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 服务评价视图对象 hq_pm_service_evaluation
*
* @author Cursor AI
* @date 2025-01-23
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = HqPmServiceEvaluation.class)
public class HqPmServiceEvaluationVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 报修工单ID,关联hq_pm_repair_order.id
*/
@ExcelProperty(value = "报修工单ID")
private Long orderId;
/**
* 评价人ID,关联sys_user.id
*/
@ExcelProperty(value = "评价人ID")
private Long evaluatorId;
/**
* 评价人姓名
*/
@ExcelProperty(value = "评价人姓名")
private String evaluatorName;
/**
* 被评价维修员ID,关联sys_user.id
*/
@ExcelProperty(value = "维修员ID")
private Long evaluatedWorkerId;
/**
* 被评价维修员姓名
*/
@ExcelProperty(value = "维修员姓名")
private String evaluatedWorkerName;
/**
* 综合评价得分(1.0-5.0)
*/
@ExcelProperty(value = "综合评价得分")
private BigDecimal overallScore;
/**
* 维修质量得分(1.0-5.0)
*/
@ExcelProperty(value = "维修质量得分")
private BigDecimal qualityScore;
/**
* 服务态度得分(1.0-5.0)
*/
@ExcelProperty(value = "服务态度得分")
private BigDecimal attitudeScore;
/**
* 响应速度得分(1.0-5.0)
*/
@ExcelProperty(value = "响应速度得分")
private BigDecimal responseSpeedScore;
/**
* 沟通效果得分(1.0-5.0)
*/
@ExcelProperty(value = "沟通效果得分")
private BigDecimal communicationScore;
/**
* 文字评价内容
*/
@ExcelProperty(value = "文字评价内容")
private String evaluationContent;
/**
* 满意度等级:1-非常满意,2-满意,3-一般,4-不满意,5-非常不满意
*/
@ExcelProperty(value = "满意度等级", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "pm_satisfaction_level")
private String satisfactionLevel;
/**
* 是否匿名评价(0-实名,1-匿名)
*/
@ExcelProperty(value = "是否匿名评价", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_yes_no")
private Integer isAnonymous;
/**
* 是否公开显示(0-不公开,1-公开)
*/
@ExcelProperty(value = "是否公开显示", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_yes_no")
private Integer isPublic;
/**
* 评价时间
*/
@ExcelProperty(value = "评价时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date evaluationTime;
/**
* 服务开始时间
*/
@ExcelProperty(value = "服务开始时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date serviceStartTime;
/**
* 服务结束时间
*/
@ExcelProperty(value = "服务结束时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date serviceEndTime;
/**
* 服务时长(分钟)
*/
@ExcelProperty(value = "服务时长(分钟)")
private Integer serviceDuration;
/**
* 正面标签(逗号分隔)
*/
@ExcelProperty(value = "正面标签")
private String positiveTags;
/**
* 负面标签(逗号分隔)
*/
@ExcelProperty(value = "负面标签")
private String negativeTags;
/**
* 改进建议
*/
@ExcelProperty(value = "改进建议")
private String improvementSuggestions;
/**
* 备注信息
*/
@ExcelProperty(value = "备注信息")
private String remark;
/**
* 租户ID
*/
private String tenantId;
/**
* 创建部门
*/
private Long createDept;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,15 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.HqPmRepairAttachment;
import org.dromara.hq.domain.vo.HqPmRepairAttachmentVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 报修附件Mapper接口
*
* @author Cursor AI
* @date 2025-01-26
*/
public interface HqPmRepairAttachmentMapper extends BaseMapperPlus<HqPmRepairAttachment, HqPmRepairAttachmentVo> {
}
@@ -0,0 +1,15 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.HqPmRepairCategory;
import org.dromara.hq.domain.vo.HqPmRepairCategoryVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 维修类型Mapper接口
*
* @author Cursor AI
* @date 2025-01-23
*/
public interface HqPmRepairCategoryMapper extends BaseMapperPlus<HqPmRepairCategory, HqPmRepairCategoryVo> {
}
@@ -1,7 +1,7 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.PmRepairOrder;
import org.dromara.hq.domain.vo.PmRepairOrderVo;
import org.dromara.hq.domain.HqPmRepairOrder;
import org.dromara.hq.domain.vo.HqPmRepairOrderVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
@@ -13,7 +13,7 @@ import java.util.Map;
* @author Cursor AI
* @date 2025-01-23
*/
public interface PmRepairOrderMapper extends BaseMapperPlus<PmRepairOrder, PmRepairOrderVo> {
public interface HqPmRepairOrderMapper extends BaseMapperPlus<HqPmRepairOrder, HqPmRepairOrderVo> {
/**
* 查询拥有"维修人员"角色的用户列表
@@ -1,6 +1,6 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.PmRepairProgress;
import org.dromara.hq.domain.HqPmRepairProgress;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
@@ -9,6 +9,6 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
* @author Cursor AI
* @date 2025-01-26
*/
public interface PmRepairProgressMapper extends BaseMapperPlus<PmRepairProgress, PmRepairProgress> {
public interface HqPmRepairProgressMapper extends BaseMapperPlus<HqPmRepairProgress, HqPmRepairProgress> {
}
@@ -1,6 +1,6 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.PmServiceEvaluation;
import org.dromara.hq.domain.HqPmServiceEvaluation;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
@@ -9,6 +9,6 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
* @author Cursor AI
* @date 2025-01-26
*/
public interface PmServiceEvaluationMapper extends BaseMapperPlus<PmServiceEvaluation, PmServiceEvaluation> {
public interface HqPmServiceEvaluationMapper extends BaseMapperPlus<HqPmServiceEvaluation, HqPmServiceEvaluation> {
}
@@ -1,15 +0,0 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.PmRepairAttachment;
import org.dromara.hq.domain.vo.PmRepairAttachmentVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 报修附件Mapper接口
*
* @author Cursor AI
* @date 2025-01-26
*/
public interface PmRepairAttachmentMapper extends BaseMapperPlus<PmRepairAttachment, PmRepairAttachmentVo> {
}
@@ -1,15 +0,0 @@
package org.dromara.hq.mapper;
import org.dromara.hq.domain.PmRepairCategory;
import org.dromara.hq.domain.vo.PmRepairCategoryVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 维修类型Mapper接口
*
* @author Cursor AI
* @date 2025-01-23
*/
public interface PmRepairCategoryMapper extends BaseMapperPlus<PmRepairCategory, PmRepairCategoryVo> {
}
@@ -3,6 +3,8 @@ package org.dromara.hq.service;
import org.dromara.hq.domain.vo.HqHallReservationVo;
import org.dromara.hq.domain.bo.HqHallReservationBo;
import org.dromara.hq.domain.bo.HqHallReservationAuditBo;
import org.dromara.hq.domain.bo.HqHallTimeCheckBo;
import org.dromara.hq.domain.vo.HqHallTimeCheckVo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@@ -74,4 +76,12 @@ public interface IHqHallReservationService {
* @return 是否审核成功
*/
Boolean auditByIds(HqHallReservationAuditBo auditBo);
/**
* 检查报告厅时间冲突
*
* @param timeCheckBo 时间检查参数
* @return 时间冲突检查结果
*/
HqHallTimeCheckVo checkTimeConflict(HqHallTimeCheckBo timeCheckBo);
}
@@ -1,7 +1,7 @@
package org.dromara.hq.service;
import org.dromara.hq.domain.vo.PmRepairAttachmentVo;
import org.dromara.hq.domain.bo.PmRepairAttachmentBo;
import org.dromara.hq.domain.vo.HqPmRepairAttachmentVo;
import org.dromara.hq.domain.bo.HqPmRepairAttachmentBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.springframework.web.multipart.MultipartFile;
@@ -15,37 +15,37 @@ import java.util.List;
* @author Cursor AI
* @date 2025-01-26
*/
public interface IPmRepairAttachmentService {
public interface IHqPmRepairAttachmentService {
/**
* 查询报修附件
*/
PmRepairAttachmentVo queryById(Long id);
HqPmRepairAttachmentVo queryById(Long id);
/**
* 查询报修附件列表
*/
TableDataInfo<PmRepairAttachmentVo> queryPageList(PmRepairAttachmentBo bo, PageQuery pageQuery);
TableDataInfo<HqPmRepairAttachmentVo> queryPageList(HqPmRepairAttachmentBo bo, PageQuery pageQuery);
/**
* 查询报修附件列表
*/
List<PmRepairAttachmentVo> queryList(PmRepairAttachmentBo bo);
List<HqPmRepairAttachmentVo> queryList(HqPmRepairAttachmentBo bo);
/**
* 根据工单ID查询附件列表
*/
List<PmRepairAttachmentVo> queryByOrderId(Long orderId);
List<HqPmRepairAttachmentVo> queryByOrderId(Long orderId);
/**
* 新增报修附件
*/
Boolean insertByBo(PmRepairAttachmentBo bo);
Boolean insertByBo(HqPmRepairAttachmentBo bo);
/**
* 修改报修附件
*/
Boolean updateByBo(PmRepairAttachmentBo bo);
Boolean updateByBo(HqPmRepairAttachmentBo bo);
/**
* 校验并批量删除报修附件信息
@@ -55,12 +55,12 @@ public interface IPmRepairAttachmentService {
/**
* 上传附件文件
*/
PmRepairAttachmentVo uploadFile(Long orderId, String uploadStage, MultipartFile file, String description);
HqPmRepairAttachmentVo uploadFile(Long orderId, String uploadStage, MultipartFile file, String description);
/**
* 批量上传附件文件
*/
List<PmRepairAttachmentVo> uploadFiles(Long orderId, String uploadStage, MultipartFile[] files,
List<HqPmRepairAttachmentVo> uploadFiles(Long orderId, String uploadStage, MultipartFile[] files,
List<String> descriptions);
/**
@@ -1,7 +1,7 @@
package org.dromara.hq.service;
import org.dromara.hq.domain.vo.PmRepairCategoryVo;
import org.dromara.hq.domain.bo.PmRepairCategoryBo;
import org.dromara.hq.domain.vo.HqPmRepairCategoryVo;
import org.dromara.hq.domain.bo.HqPmRepairCategoryBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@@ -14,7 +14,7 @@ import java.util.List;
* @author Cursor AI
* @date 2025-01-23
*/
public interface IPmRepairCategoryService {
public interface IHqPmRepairCategoryService {
/**
* 查询维修类型
@@ -22,7 +22,7 @@ public interface IPmRepairCategoryService {
* @param id 主键
* @return 维修类型
*/
PmRepairCategoryVo queryById(Long id);
HqPmRepairCategoryVo queryById(Long id);
/**
* 分页查询维修类型列表
@@ -31,7 +31,7 @@ public interface IPmRepairCategoryService {
* @param pageQuery 分页参数
* @return 维修类型分页列表
*/
TableDataInfo<PmRepairCategoryVo> queryPageList(PmRepairCategoryBo bo, PageQuery pageQuery);
TableDataInfo<HqPmRepairCategoryVo> queryPageList(HqPmRepairCategoryBo bo, PageQuery pageQuery);
/**
* 查询符合条件的维修类型列表
@@ -39,7 +39,7 @@ public interface IPmRepairCategoryService {
* @param bo 查询条件
* @return 维修类型列表
*/
List<PmRepairCategoryVo> queryList(PmRepairCategoryBo bo);
List<HqPmRepairCategoryVo> queryList(HqPmRepairCategoryBo bo);
/**
* 新增维修类型
@@ -47,7 +47,7 @@ public interface IPmRepairCategoryService {
* @param bo 维修类型
* @return 是否新增成功
*/
Boolean insertByBo(PmRepairCategoryBo bo);
Boolean insertByBo(HqPmRepairCategoryBo bo);
/**
* 修改维修类型
@@ -55,7 +55,7 @@ public interface IPmRepairCategoryService {
* @param bo 维修类型
* @return 是否修改成功
*/
Boolean updateByBo(PmRepairCategoryBo bo);
Boolean updateByBo(HqPmRepairCategoryBo bo);
/**
* 校验并批量删除维修类型信息
@@ -71,7 +71,7 @@ public interface IPmRepairCategoryService {
*
* @return 维修类型树形列表
*/
List<PmRepairCategoryVo> queryTree();
List<HqPmRepairCategoryVo> queryTree();
/**
* 更新使用次数
@@ -1,7 +1,7 @@
package org.dromara.hq.service;
import org.dromara.hq.domain.vo.PmRepairOrderVo;
import org.dromara.hq.domain.bo.PmRepairOrderBo;
import org.dromara.hq.domain.vo.HqPmRepairOrderVo;
import org.dromara.hq.domain.bo.HqPmRepairOrderBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@@ -14,7 +14,7 @@ import java.util.List;
* @author Cursor AI
* @date 2025-01-23
*/
public interface IPmRepairOrderService {
public interface IHqPmRepairOrderService {
/**
* 查询报修工单
@@ -22,7 +22,7 @@ public interface IPmRepairOrderService {
* @param id 主键
* @return 报修工单
*/
PmRepairOrderVo queryById(Long id);
HqPmRepairOrderVo queryById(Long id);
/**
* 分页查询报修工单列表
@@ -31,7 +31,7 @@ public interface IPmRepairOrderService {
* @param pageQuery 分页参数
* @return 报修工单分页列表
*/
TableDataInfo<PmRepairOrderVo> queryPageList(PmRepairOrderBo bo, PageQuery pageQuery);
TableDataInfo<HqPmRepairOrderVo> queryPageList(HqPmRepairOrderBo bo, PageQuery pageQuery);
/**
* 查询符合条件的报修工单列表
@@ -39,7 +39,7 @@ public interface IPmRepairOrderService {
* @param bo 查询条件
* @return 报修工单列表
*/
List<PmRepairOrderVo> queryList(PmRepairOrderBo bo);
List<HqPmRepairOrderVo> queryList(HqPmRepairOrderBo bo);
/**
* 新增报修工单
@@ -47,7 +47,7 @@ public interface IPmRepairOrderService {
* @param bo 报修工单
* @return 是否新增成功
*/
Boolean insertByBo(PmRepairOrderBo bo);
Boolean insertByBo(HqPmRepairOrderBo bo);
/**
* 修改报修工单
@@ -55,7 +55,7 @@ public interface IPmRepairOrderService {
* @param bo 报修工单
* @return 是否修改成功
*/
Boolean updateByBo(PmRepairOrderBo bo);
Boolean updateByBo(HqPmRepairOrderBo bo);
/**
* 校验并批量删除报修工单信息
@@ -0,0 +1,64 @@
package org.dromara.hq.service;
import org.dromara.hq.domain.vo.HqPmRepairProgressVo;
import org.dromara.hq.domain.bo.HqPmRepairProgressBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 维修进度记录Service接口
*
* @author Cursor AI
* @date 2025-01-23
*/
public interface IHqPmRepairProgressService {
/**
* 查询维修进度记录
*/
HqPmRepairProgressVo queryById(Long id);
/**
* 查询维修进度记录列表
*/
TableDataInfo<HqPmRepairProgressVo> queryPageList(HqPmRepairProgressBo bo, PageQuery pageQuery);
/**
* 查询维修进度记录列表
*/
List<HqPmRepairProgressVo> queryList(HqPmRepairProgressBo bo);
/**
* 根据工单ID查询进度记录列表
*/
List<HqPmRepairProgressVo> queryListByOrderId(Long orderId);
/**
* 新增维修进度记录
*/
Boolean insertByBo(HqPmRepairProgressBo bo);
/**
* 修改维修进度记录
*/
Boolean updateByBo(HqPmRepairProgressBo bo);
/**
* 校验并批量删除维修进度记录信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 批量删除维修进度记录信息
*/
Boolean deleteByIds(Collection<Long> ids);
/**
* 删除维修进度记录信息
*/
Boolean deleteById(Long id);
}
@@ -0,0 +1,69 @@
package org.dromara.hq.service;
import org.dromara.hq.domain.vo.HqPmServiceEvaluationVo;
import org.dromara.hq.domain.bo.HqPmServiceEvaluationBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 服务评价Service接口
*
* @author Cursor AI
* @date 2025-01-23
*/
public interface IHqPmServiceEvaluationService {
/**
* 查询服务评价
*/
HqPmServiceEvaluationVo queryById(Long id);
/**
* 查询服务评价列表
*/
TableDataInfo<HqPmServiceEvaluationVo> queryPageList(HqPmServiceEvaluationBo bo, PageQuery pageQuery);
/**
* 查询服务评价列表
*/
List<HqPmServiceEvaluationVo> queryList(HqPmServiceEvaluationBo bo);
/**
* 根据工单ID查询评价信息
*/
HqPmServiceEvaluationVo queryByOrderId(Long orderId);
/**
* 根据维修员ID查询评价列表
*/
List<HqPmServiceEvaluationVo> queryListByWorkerId(Long workerId);
/**
* 新增服务评价
*/
Boolean insertByBo(HqPmServiceEvaluationBo bo);
/**
* 修改服务评价
*/
Boolean updateByBo(HqPmServiceEvaluationBo bo);
/**
* 校验并批量删除服务评价信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 批量删除服务评价信息
*/
Boolean deleteByIds(Collection<Long> ids);
/**
* 删除服务评价信息
*/
Boolean deleteById(Long id);
}
@@ -12,7 +12,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.hq.domain.bo.HqHallReservationBo;
import org.dromara.hq.domain.bo.HqHallReservationAuditBo;
import org.dromara.hq.domain.bo.HqHallTimeCheckBo;
import org.dromara.hq.domain.vo.HqHallReservationVo;
import org.dromara.hq.domain.vo.HqHallTimeCheckVo;
import org.dromara.hq.domain.HqHallReservation;
import org.dromara.hq.mapper.HqHallReservationMapper;
import org.dromara.hq.service.IHqHallReservationService;
@@ -223,4 +225,49 @@ public class HqHallReservationServiceImpl implements IHqHallReservationService {
return result;
}
/**
* 检查报告厅时间冲突
*
* @param timeCheckBo 时间检查参数
* @return 时间冲突检查结果
*/
@Override
public HqHallTimeCheckVo checkTimeConflict(HqHallTimeCheckBo timeCheckBo) {
log.info("开始检查报告厅时间冲突,参数:{}", timeCheckBo);
// 构建查询条件:同一报告厅,时间有重叠,状态为已批准的预约
LambdaQueryWrapper<HqHallReservation> lqw = Wrappers.lambdaQuery();
// 1. 同一报告厅
lqw.eq(HqHallReservation::getHallId, timeCheckBo.getHallId());
// 2. 状态为已批准(状态为'1')
lqw.eq(HqHallReservation::getStatus, "1");
// 3. 如果有排除ID,则排除该记录(用于编辑时排除自己)
if (timeCheckBo.getExcludeId() != null) {
lqw.ne(HqHallReservation::getId, timeCheckBo.getExcludeId());
}
// 4. 时间重叠判断:
// 两个时间段重叠的条件:新开始时间 < 已存在结束时间 AND 新结束时间 > 已存在开始时间
// 即:startTime < existingEndTime AND endTime > existingStartTime
lqw.and(wrapper -> wrapper
.lt(HqHallReservation::getStartTime, timeCheckBo.getEndTime())
.gt(HqHallReservation::getEndTime, timeCheckBo.getStartTime()));
// 查询冲突的预约记录
List<HqHallReservationVo> conflictList = baseMapper.selectVoList(lqw);
// 构建返回结果
if (conflictList.isEmpty()) {
log.info("未发现时间冲突");
return HqHallTimeCheckVo.noConflict();
} else {
String conflictMessage = String.format("发现 %d 个时间冲突的已批准预约", conflictList.size());
log.info("发现时间冲突,冲突记录数:{}", conflictList.size());
return HqHallTimeCheckVo.hasConflict(conflictList, conflictMessage);
}
}
}
@@ -16,12 +16,12 @@ import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.hq.domain.PmRepairAttachment;
import org.dromara.hq.domain.bo.PmRepairAttachmentBo;
import org.dromara.hq.domain.vo.PmRepairAttachmentVo;
import org.dromara.hq.mapper.PmRepairAttachmentMapper;
import org.dromara.hq.mapper.PmRepairOrderMapper;
import org.dromara.hq.service.IPmRepairAttachmentService;
import org.dromara.hq.domain.HqPmRepairAttachment;
import org.dromara.hq.domain.bo.HqPmRepairAttachmentBo;
import org.dromara.hq.domain.vo.HqPmRepairAttachmentVo;
import org.dromara.hq.mapper.HqPmRepairAttachmentMapper;
import org.dromara.hq.mapper.HqPmRepairOrderMapper;
import org.dromara.hq.service.IHqPmRepairAttachmentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@@ -40,16 +40,16 @@ import java.util.List;
@Slf4j
@RequiredArgsConstructor
@Service
public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService {
public class HqPmRepairAttachmentServiceImpl implements IHqPmRepairAttachmentService {
private final PmRepairAttachmentMapper baseMapper;
private final PmRepairOrderMapper repairOrderMapper;
private final HqPmRepairAttachmentMapper baseMapper;
private final HqPmRepairOrderMapper repairOrderMapper;
/**
* 查询报修附件
*/
@Override
public PmRepairAttachmentVo queryById(Long id) {
public HqPmRepairAttachmentVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
@@ -57,9 +57,9 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
* 查询报修附件列表
*/
@Override
public TableDataInfo<PmRepairAttachmentVo> queryPageList(PmRepairAttachmentBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<PmRepairAttachment> lqw = buildQueryWrapper(bo);
Page<PmRepairAttachmentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
public TableDataInfo<HqPmRepairAttachmentVo> queryPageList(HqPmRepairAttachmentBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HqPmRepairAttachment> lqw = buildQueryWrapper(bo);
Page<HqPmRepairAttachmentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@@ -67,8 +67,8 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
* 查询报修附件列表
*/
@Override
public List<PmRepairAttachmentVo> queryList(PmRepairAttachmentBo bo) {
LambdaQueryWrapper<PmRepairAttachment> lqw = buildQueryWrapper(bo);
public List<HqPmRepairAttachmentVo> queryList(HqPmRepairAttachmentBo bo) {
LambdaQueryWrapper<HqPmRepairAttachment> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
@@ -76,22 +76,22 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
* 根据工单ID查询附件列表
*/
@Override
public List<PmRepairAttachmentVo> queryByOrderId(Long orderId) {
LambdaQueryWrapper<PmRepairAttachment> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairAttachment::getOrderId, orderId);
lqw.orderByAsc(PmRepairAttachment::getSortOrder)
.orderByAsc(PmRepairAttachment::getCreateTime);
public List<HqPmRepairAttachmentVo> queryByOrderId(Long orderId) {
LambdaQueryWrapper<HqPmRepairAttachment> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairAttachment::getOrderId, orderId);
lqw.orderByAsc(HqPmRepairAttachment::getSortOrder)
.orderByAsc(HqPmRepairAttachment::getCreateTime);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<PmRepairAttachment> buildQueryWrapper(PmRepairAttachmentBo bo) {
LambdaQueryWrapper<PmRepairAttachment> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getOrderId() != null, PmRepairAttachment::getOrderId, bo.getOrderId());
lqw.like(StringUtils.isNotBlank(bo.getFileName()), PmRepairAttachment::getFileName, bo.getFileName());
lqw.eq(StringUtils.isNotBlank(bo.getFileType()), PmRepairAttachment::getFileType, bo.getFileType());
lqw.eq(StringUtils.isNotBlank(bo.getUploadStage()), PmRepairAttachment::getUploadStage, bo.getUploadStage());
lqw.eq(bo.getUploaderId() != null, PmRepairAttachment::getUploaderId, bo.getUploaderId());
lqw.orderByAsc(PmRepairAttachment::getSortOrder).orderByDesc(PmRepairAttachment::getCreateTime);
private LambdaQueryWrapper<HqPmRepairAttachment> buildQueryWrapper(HqPmRepairAttachmentBo bo) {
LambdaQueryWrapper<HqPmRepairAttachment> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getOrderId() != null, HqPmRepairAttachment::getOrderId, bo.getOrderId());
lqw.like(StringUtils.isNotBlank(bo.getFileName()), HqPmRepairAttachment::getFileName, bo.getFileName());
lqw.eq(StringUtils.isNotBlank(bo.getFileType()), HqPmRepairAttachment::getFileType, bo.getFileType());
lqw.eq(StringUtils.isNotBlank(bo.getUploadStage()), HqPmRepairAttachment::getUploadStage, bo.getUploadStage());
lqw.eq(bo.getUploaderId() != null, HqPmRepairAttachment::getUploaderId, bo.getUploaderId());
lqw.orderByAsc(HqPmRepairAttachment::getSortOrder).orderByDesc(HqPmRepairAttachment::getCreateTime);
return lqw;
}
@@ -99,8 +99,8 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
* 新增报修附件
*/
@Override
public Boolean insertByBo(PmRepairAttachmentBo bo) {
PmRepairAttachment add = MapstructUtils.convert(bo, PmRepairAttachment.class);
public Boolean insertByBo(HqPmRepairAttachmentBo bo) {
HqPmRepairAttachment add = MapstructUtils.convert(bo, HqPmRepairAttachment.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
@@ -115,8 +115,8 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
* 修改报修附件
*/
@Override
public Boolean updateByBo(PmRepairAttachmentBo bo) {
PmRepairAttachment update = MapstructUtils.convert(bo, PmRepairAttachment.class);
public Boolean updateByBo(HqPmRepairAttachmentBo bo) {
HqPmRepairAttachment update = MapstructUtils.convert(bo, HqPmRepairAttachment.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
@@ -124,7 +124,7 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(PmRepairAttachment entity) {
private void validEntityBeforeSave(HqPmRepairAttachment entity) {
// 设置默认值
if (entity.getSortOrder() == null) {
entity.setSortOrder(0);
@@ -147,9 +147,9 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
// 先获取要删除的附件信息(在删除前)
List<PmRepairAttachmentVo> attachments = new ArrayList<>();
List<HqPmRepairAttachmentVo> attachments = new ArrayList<>();
for (Long id : ids) {
PmRepairAttachmentVo attachment = baseMapper.selectVoById(id);
HqPmRepairAttachmentVo attachment = baseMapper.selectVoById(id);
if (attachment != null) {
attachments.add(attachment);
}
@@ -157,7 +157,7 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
if (isValid) {
// 删除OSS文件
for (PmRepairAttachmentVo attachment : attachments) {
for (HqPmRepairAttachmentVo attachment : attachments) {
try {
if (StringUtils.isNotBlank(attachment.getFilePath())) {
OssFactory.instance().delete(attachment.getFilePath());
@@ -176,7 +176,7 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
if (flag) {
// 更新相关工单的附件数量
attachments.stream()
.map(PmRepairAttachmentVo::getOrderId)
.map(HqPmRepairAttachmentVo::getOrderId)
.distinct()
.forEach(this::updateOrderAttachmentCount);
}
@@ -188,7 +188,7 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
*/
@Override
@Transactional(rollbackFor = Exception.class)
public PmRepairAttachmentVo uploadFile(Long orderId, String uploadStage, MultipartFile file, String description) {
public HqPmRepairAttachmentVo uploadFile(Long orderId, String uploadStage, MultipartFile file, String description) {
try {
// 文件基本信息
String originalFilename = file.getOriginalFilename();
@@ -200,7 +200,7 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
UploadResult uploadResult = OssFactory.instance().uploadSuffix(file.getBytes(), fileExtension, contentType);
// 构建附件信息
PmRepairAttachmentBo bo = new PmRepairAttachmentBo();
HqPmRepairAttachmentBo bo = new HqPmRepairAttachmentBo();
bo.setOrderId(orderId);
bo.setFileName(originalFilename);
bo.setFilePath(uploadResult.getUrl());
@@ -231,16 +231,16 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
*/
@Override
@Transactional(rollbackFor = Exception.class)
public List<PmRepairAttachmentVo> uploadFiles(Long orderId, String uploadStage, MultipartFile[] files,
public List<HqPmRepairAttachmentVo> uploadFiles(Long orderId, String uploadStage, MultipartFile[] files,
List<String> descriptions) {
List<PmRepairAttachmentVo> results = new ArrayList<>();
List<HqPmRepairAttachmentVo> results = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
String description = (descriptions != null && i < descriptions.size()) ? descriptions.get(i) : null;
try {
PmRepairAttachmentVo result = uploadFile(orderId, uploadStage, file, description);
HqPmRepairAttachmentVo result = uploadFile(orderId, uploadStage, file, description);
results.add(result);
} catch (Exception e) {
log.error("批量上传第{}个文件失败: {}", i + 1, e.getMessage());
@@ -257,7 +257,7 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteAttachment(Long id) {
PmRepairAttachmentVo attachment = queryById(id);
HqPmRepairAttachmentVo attachment = queryById(id);
if (attachment == null) {
return false;
}
@@ -292,14 +292,14 @@ public class PmRepairAttachmentServiceImpl implements IPmRepairAttachmentService
return;
}
LambdaQueryWrapper<PmRepairAttachment> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairAttachment::getOrderId, orderId);
LambdaQueryWrapper<HqPmRepairAttachment> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairAttachment::getOrderId, orderId);
long count = baseMapper.selectCount(lqw);
// 更新工单的附件数量
LambdaUpdateWrapper<org.dromara.hq.domain.PmRepairOrder> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(org.dromara.hq.domain.PmRepairOrder::getId, orderId)
.set(org.dromara.hq.domain.PmRepairOrder::getAttachmentCount, (int) count);
LambdaUpdateWrapper<org.dromara.hq.domain.HqPmRepairOrder> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(org.dromara.hq.domain.HqPmRepairOrder::getId, orderId)
.set(org.dromara.hq.domain.HqPmRepairOrder::getAttachmentCount, (int) count);
repairOrderMapper.update(null, updateWrapper);
}
@@ -11,11 +11,11 @@ import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.hq.domain.bo.PmRepairCategoryBo;
import org.dromara.hq.domain.vo.PmRepairCategoryVo;
import org.dromara.hq.domain.PmRepairCategory;
import org.dromara.hq.mapper.PmRepairCategoryMapper;
import org.dromara.hq.service.IPmRepairCategoryService;
import org.dromara.hq.domain.bo.HqPmRepairCategoryBo;
import org.dromara.hq.domain.vo.HqPmRepairCategoryVo;
import org.dromara.hq.domain.HqPmRepairCategory;
import org.dromara.hq.mapper.HqPmRepairCategoryMapper;
import org.dromara.hq.service.IHqPmRepairCategoryService;
import org.dromara.common.core.exception.ServiceException;
import java.util.List;
@@ -32,9 +32,9 @@ import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
@Service
public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
public class HqPmRepairCategoryServiceImpl implements IHqPmRepairCategoryService {
private final PmRepairCategoryMapper baseMapper;
private final HqPmRepairCategoryMapper baseMapper;
/**
* 查询维修类型
@@ -43,7 +43,7 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
* @return 维修类型
*/
@Override
public PmRepairCategoryVo queryById(Long id) {
public HqPmRepairCategoryVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
@@ -55,9 +55,9 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
* @return 维修类型分页列表
*/
@Override
public TableDataInfo<PmRepairCategoryVo> queryPageList(PmRepairCategoryBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<PmRepairCategory> lqw = buildQueryWrapper(bo);
Page<PmRepairCategoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
public TableDataInfo<HqPmRepairCategoryVo> queryPageList(HqPmRepairCategoryBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HqPmRepairCategory> lqw = buildQueryWrapper(bo);
Page<HqPmRepairCategoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@@ -68,26 +68,28 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
* @return 维修类型列表
*/
@Override
public List<PmRepairCategoryVo> queryList(PmRepairCategoryBo bo) {
LambdaQueryWrapper<PmRepairCategory> lqw = buildQueryWrapper(bo);
public List<HqPmRepairCategoryVo> queryList(HqPmRepairCategoryBo bo) {
LambdaQueryWrapper<HqPmRepairCategory> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<PmRepairCategory> buildQueryWrapper(PmRepairCategoryBo bo) {
LambdaQueryWrapper<PmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(PmRepairCategory::getSortOrder).orderByAsc(PmRepairCategory::getId);
lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), PmRepairCategory::getCategoryName, bo.getCategoryName());
lqw.eq(StringUtils.isNotBlank(bo.getCategoryCode()), PmRepairCategory::getCategoryCode, bo.getCategoryCode());
lqw.like(StringUtils.isNotBlank(bo.getCategoryDescription()), PmRepairCategory::getCategoryDescription,
private LambdaQueryWrapper<HqPmRepairCategory> buildQueryWrapper(HqPmRepairCategoryBo bo) {
LambdaQueryWrapper<HqPmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(HqPmRepairCategory::getSortOrder).orderByAsc(HqPmRepairCategory::getId);
lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), HqPmRepairCategory::getCategoryName,
bo.getCategoryName());
lqw.like(StringUtils.isNotBlank(bo.getCategoryCode()), HqPmRepairCategory::getCategoryCode,
bo.getCategoryCode());
lqw.like(StringUtils.isNotBlank(bo.getCategoryDescription()), HqPmRepairCategory::getCategoryDescription,
bo.getCategoryDescription());
lqw.eq(bo.getParentId() != null, PmRepairCategory::getParentId, bo.getParentId());
lqw.eq(bo.getLevelDepth() != null, PmRepairCategory::getLevelDepth, bo.getLevelDepth());
lqw.eq(StringUtils.isNotBlank(bo.getSkillLevel()), PmRepairCategory::getSkillLevel, bo.getSkillLevel());
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), PmRepairCategory::getStatus, bo.getStatus());
lqw.eq(bo.getParentId() != null, HqPmRepairCategory::getParentId, bo.getParentId());
lqw.eq(bo.getLevelDepth() != null, HqPmRepairCategory::getLevelDepth, bo.getLevelDepth());
lqw.eq(StringUtils.isNotBlank(bo.getSkillLevel()), HqPmRepairCategory::getSkillLevel, bo.getSkillLevel());
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), HqPmRepairCategory::getStatus, bo.getStatus());
// IN查询条件
lqw.in(CollUtil.isNotEmpty(bo.getStatusIn()), PmRepairCategory::getStatus, bo.getStatusIn());
lqw.in(CollUtil.isNotEmpty(bo.getParentIdIn()), PmRepairCategory::getParentId, bo.getParentIdIn());
lqw.in(CollUtil.isNotEmpty(bo.getStatusIn()), HqPmRepairCategory::getStatus, bo.getStatusIn());
lqw.in(CollUtil.isNotEmpty(bo.getParentIdIn()), HqPmRepairCategory::getParentId, bo.getParentIdIn());
return lqw;
}
@@ -99,8 +101,8 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(PmRepairCategoryBo bo) {
PmRepairCategory add = MapstructUtils.convert(bo, PmRepairCategory.class);
public Boolean insertByBo(HqPmRepairCategoryBo bo) {
HqPmRepairCategory add = MapstructUtils.convert(bo, HqPmRepairCategory.class);
validEntityBeforeSave(add);
// 设置层级信息
@@ -140,12 +142,12 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(PmRepairCategoryBo bo) {
PmRepairCategory update = MapstructUtils.convert(bo, PmRepairCategory.class);
public Boolean updateByBo(HqPmRepairCategoryBo bo) {
HqPmRepairCategory update = MapstructUtils.convert(bo, HqPmRepairCategory.class);
validEntityBeforeSave(update);
// 如果修改了父级,需要重新计算层级信息
PmRepairCategory oldEntity = baseMapper.selectById(update.getId());
HqPmRepairCategory oldEntity = baseMapper.selectById(update.getId());
if (oldEntity != null && !oldEntity.getParentId().equals(update.getParentId())) {
buildHierarchyInfo(update);
// 更新新旧父级的子类数量
@@ -159,13 +161,13 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(PmRepairCategory entity) {
private void validEntityBeforeSave(HqPmRepairCategory entity) {
// 校验类型编码唯一性
if (StringUtils.isNotBlank(entity.getCategoryCode())) {
LambdaQueryWrapper<PmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairCategory::getCategoryCode, entity.getCategoryCode());
LambdaQueryWrapper<HqPmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairCategory::getCategoryCode, entity.getCategoryCode());
if (entity.getId() != null) {
lqw.ne(PmRepairCategory::getId, entity.getId());
lqw.ne(HqPmRepairCategory::getId, entity.getId());
}
boolean exists = baseMapper.exists(lqw);
if (exists) {
@@ -182,13 +184,13 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
/**
* 构建层级信息
*/
private void buildHierarchyInfo(PmRepairCategory entity) {
private void buildHierarchyInfo(HqPmRepairCategory entity) {
if (entity.getParentId() == null || entity.getParentId() == 0L) {
entity.setParentId(0L);
entity.setLevelDepth(1);
entity.setAncestorIds("0");
} else {
PmRepairCategory parent = baseMapper.selectById(entity.getParentId());
HqPmRepairCategory parent = baseMapper.selectById(entity.getParentId());
if (parent != null) {
entity.setLevelDepth(parent.getLevelDepth() + 1);
entity.setAncestorIds(parent.getAncestorIds() + "," + parent.getId());
@@ -204,11 +206,11 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
return;
}
LambdaQueryWrapper<PmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairCategory::getParentId, parentId);
LambdaQueryWrapper<HqPmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairCategory::getParentId, parentId);
long count = baseMapper.selectCount(lqw);
PmRepairCategory parent = new PmRepairCategory();
HqPmRepairCategory parent = new HqPmRepairCategory();
parent.setId(parentId);
parent.setChildrenCount((int) count);
baseMapper.updateById(parent);
@@ -226,7 +228,7 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
if (isValid) {
// 校验是否有子类
for (Long id : ids) {
PmRepairCategory category = baseMapper.selectById(id);
HqPmRepairCategory category = baseMapper.selectById(id);
if (category != null && category.getChildrenCount() > 0) {
throw new ServiceException("存在子类型,不允许删除:" + category.getCategoryName());
}
@@ -234,10 +236,10 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
}
// 删除后更新父级的子类数量
List<PmRepairCategory> categories = baseMapper.selectBatchIds(ids);
List<HqPmRepairCategory> categories = baseMapper.selectBatchIds(ids);
boolean flag = baseMapper.deleteByIds(ids) > 0;
if (flag) {
for (PmRepairCategory category : categories) {
for (HqPmRepairCategory category : categories) {
updateParentChildrenCount(category.getParentId());
}
}
@@ -250,11 +252,11 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
* @return 维修类型树形列表
*/
@Override
public List<PmRepairCategoryVo> queryTree() {
LambdaQueryWrapper<PmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairCategory::getStatus, "1");
lqw.orderByAsc(PmRepairCategory::getSortOrder).orderByAsc(PmRepairCategory::getId);
List<PmRepairCategoryVo> allList = baseMapper.selectVoList(lqw);
public List<HqPmRepairCategoryVo> queryTree() {
LambdaQueryWrapper<HqPmRepairCategory> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairCategory::getStatus, "1");
lqw.orderByAsc(HqPmRepairCategory::getSortOrder).orderByAsc(HqPmRepairCategory::getId);
List<HqPmRepairCategoryVo> allList = baseMapper.selectVoList(lqw);
return buildTree(allList, 0L);
}
@@ -262,11 +264,11 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
/**
* 构建树形结构
*/
private List<PmRepairCategoryVo> buildTree(List<PmRepairCategoryVo> allList, Long parentId) {
private List<HqPmRepairCategoryVo> buildTree(List<HqPmRepairCategoryVo> allList, Long parentId) {
return allList.stream()
.filter(item -> parentId.equals(item.getParentId()))
.map(item -> {
List<PmRepairCategoryVo> children = buildTree(allList, item.getId());
List<HqPmRepairCategoryVo> children = buildTree(allList, item.getId());
item.setChildren(children);
return item;
})
@@ -281,12 +283,12 @@ public class PmRepairCategoryServiceImpl implements IPmRepairCategoryService {
*/
@Override
public Boolean updateUsageCount(Long categoryId) {
PmRepairCategory category = baseMapper.selectById(categoryId);
HqPmRepairCategory category = baseMapper.selectById(categoryId);
if (category == null) {
return false;
}
PmRepairCategory update = new PmRepairCategory();
HqPmRepairCategory update = new HqPmRepairCategory();
update.setId(categoryId);
update.setUsageCount(category.getUsageCount() + 1);
update.setLastUsedTime(new Date());
@@ -12,16 +12,16 @@ import cn.hutool.core.date.DateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.hq.domain.bo.PmRepairOrderBo;
import org.dromara.hq.domain.vo.PmRepairOrderVo;
import org.dromara.hq.domain.PmRepairOrder;
import org.dromara.hq.domain.PmRepairProgress;
import org.dromara.hq.domain.PmServiceEvaluation;
import org.dromara.hq.mapper.PmRepairOrderMapper;
import org.dromara.hq.mapper.PmRepairProgressMapper;
import org.dromara.hq.mapper.PmServiceEvaluationMapper;
import org.dromara.hq.service.IPmRepairOrderService;
import org.dromara.hq.service.IPmRepairCategoryService;
import org.dromara.hq.domain.bo.HqPmRepairOrderBo;
import org.dromara.hq.domain.vo.HqPmRepairOrderVo;
import org.dromara.hq.domain.HqPmRepairOrder;
import org.dromara.hq.domain.HqPmRepairProgress;
import org.dromara.hq.domain.HqPmServiceEvaluation;
import org.dromara.hq.mapper.HqPmRepairOrderMapper;
import org.dromara.hq.mapper.HqPmRepairProgressMapper;
import org.dromara.hq.mapper.HqPmServiceEvaluationMapper;
import org.dromara.hq.service.IHqPmRepairOrderService;
import org.dromara.hq.service.IHqPmRepairCategoryService;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.UserService;
@@ -39,13 +39,13 @@ import java.util.Date;
@Slf4j
@RequiredArgsConstructor
@Service
public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
public class HqPmRepairOrderServiceImpl implements IHqPmRepairOrderService {
private final PmRepairOrderMapper baseMapper;
private final PmRepairProgressMapper repairProgressMapper;
private final IPmRepairCategoryService repairCategoryService;
private final HqPmRepairOrderMapper baseMapper;
private final HqPmRepairProgressMapper repairProgressMapper;
private final IHqPmRepairCategoryService repairCategoryService;
private final UserService userService;
private final PmServiceEvaluationMapper serviceEvaluationMapper;
private final HqPmServiceEvaluationMapper serviceEvaluationMapper;
/**
* 查询报修工单
@@ -54,8 +54,8 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
* @return 报修工单
*/
@Override
public PmRepairOrderVo queryById(Long id) {
PmRepairOrderVo orderVo = baseMapper.selectVoById(id);
public HqPmRepairOrderVo queryById(Long id) {
HqPmRepairOrderVo orderVo = baseMapper.selectVoById(id);
if (orderVo == null) {
return null;
}
@@ -74,7 +74,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
* @param orderVo 工单数据
* @return 是否有权限
*/
private boolean hasDataPermission(PmRepairOrderVo orderVo) {
private boolean hasDataPermission(HqPmRepairOrderVo orderVo) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
@@ -119,9 +119,9 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
* @return 报修工单分页列表
*/
@Override
public TableDataInfo<PmRepairOrderVo> queryPageList(PmRepairOrderBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<PmRepairOrder> lqw = buildQueryWrapper(bo);
Page<PmRepairOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
public TableDataInfo<HqPmRepairOrderVo> queryPageList(HqPmRepairOrderBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HqPmRepairOrder> lqw = buildQueryWrapper(bo);
Page<HqPmRepairOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@@ -132,33 +132,34 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
* @return 报修工单列表
*/
@Override
public List<PmRepairOrderVo> queryList(PmRepairOrderBo bo) {
LambdaQueryWrapper<PmRepairOrder> lqw = buildQueryWrapper(bo);
public List<HqPmRepairOrderVo> queryList(HqPmRepairOrderBo bo) {
LambdaQueryWrapper<HqPmRepairOrder> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<PmRepairOrder> buildQueryWrapper(PmRepairOrderBo bo) {
LambdaQueryWrapper<PmRepairOrder> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(PmRepairOrder::getCreateTime);
lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), PmRepairOrder::getOrderNo, bo.getOrderNo());
lqw.like(StringUtils.isNotBlank(bo.getTitle()), PmRepairOrder::getTitle, bo.getTitle());
lqw.like(StringUtils.isNotBlank(bo.getDescription()), PmRepairOrder::getDescription, bo.getDescription());
lqw.like(StringUtils.isNotBlank(bo.getRepairLocation()), PmRepairOrder::getRepairLocation,
private LambdaQueryWrapper<HqPmRepairOrder> buildQueryWrapper(HqPmRepairOrderBo bo) {
LambdaQueryWrapper<HqPmRepairOrder> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(HqPmRepairOrder::getCreateTime);
lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HqPmRepairOrder::getOrderNo, bo.getOrderNo());
lqw.like(StringUtils.isNotBlank(bo.getTitle()), HqPmRepairOrder::getTitle, bo.getTitle());
lqw.like(StringUtils.isNotBlank(bo.getDescription()), HqPmRepairOrder::getDescription, bo.getDescription());
lqw.like(StringUtils.isNotBlank(bo.getRepairLocation()), HqPmRepairOrder::getRepairLocation,
bo.getRepairLocation());
lqw.eq(bo.getCategoryId() != null, PmRepairOrder::getCategoryId, bo.getCategoryId());
lqw.eq(bo.getPriorityLevel() != null, PmRepairOrder::getPriorityLevel, bo.getPriorityLevel());
lqw.eq(bo.getReporterId() != null, PmRepairOrder::getReporterId, bo.getReporterId());
lqw.like(StringUtils.isNotBlank(bo.getReporterName()), PmRepairOrder::getReporterName, bo.getReporterName());
lqw.like(StringUtils.isNotBlank(bo.getReporterPhone()), PmRepairOrder::getReporterPhone, bo.getReporterPhone());
lqw.eq(StringUtils.isNotBlank(bo.getOrderStatus()), PmRepairOrder::getOrderStatus, bo.getOrderStatus());
lqw.eq(bo.getAssigneeId() != null, PmRepairOrder::getAssigneeId, bo.getAssigneeId());
lqw.like(StringUtils.isNotBlank(bo.getAssigneeName()), PmRepairOrder::getAssigneeName, bo.getAssigneeName());
lqw.eq(bo.getCategoryId() != null, HqPmRepairOrder::getCategoryId, bo.getCategoryId());
lqw.eq(bo.getPriorityLevel() != null, HqPmRepairOrder::getPriorityLevel, bo.getPriorityLevel());
lqw.eq(bo.getReporterId() != null, HqPmRepairOrder::getReporterId, bo.getReporterId());
lqw.like(StringUtils.isNotBlank(bo.getReporterName()), HqPmRepairOrder::getReporterName, bo.getReporterName());
lqw.like(StringUtils.isNotBlank(bo.getReporterPhone()), HqPmRepairOrder::getReporterPhone,
bo.getReporterPhone());
lqw.eq(StringUtils.isNotBlank(bo.getOrderStatus()), HqPmRepairOrder::getOrderStatus, bo.getOrderStatus());
lqw.eq(bo.getAssigneeId() != null, HqPmRepairOrder::getAssigneeId, bo.getAssigneeId());
lqw.like(StringUtils.isNotBlank(bo.getAssigneeName()), HqPmRepairOrder::getAssigneeName, bo.getAssigneeName());
// IN查询条件
lqw.in(CollUtil.isNotEmpty(bo.getOrderStatusIn()), PmRepairOrder::getOrderStatus, bo.getOrderStatusIn());
lqw.in(CollUtil.isNotEmpty(bo.getCategoryIdIn()), PmRepairOrder::getCategoryId, bo.getCategoryIdIn());
lqw.in(CollUtil.isNotEmpty(bo.getReporterIdIn()), PmRepairOrder::getReporterId, bo.getReporterIdIn());
lqw.in(CollUtil.isNotEmpty(bo.getAssigneeIdIn()), PmRepairOrder::getAssigneeId, bo.getAssigneeIdIn());
lqw.in(CollUtil.isNotEmpty(bo.getOrderStatusIn()), HqPmRepairOrder::getOrderStatus, bo.getOrderStatusIn());
lqw.in(CollUtil.isNotEmpty(bo.getCategoryIdIn()), HqPmRepairOrder::getCategoryId, bo.getCategoryIdIn());
lqw.in(CollUtil.isNotEmpty(bo.getReporterIdIn()), HqPmRepairOrder::getReporterId, bo.getReporterIdIn());
lqw.in(CollUtil.isNotEmpty(bo.getAssigneeIdIn()), HqPmRepairOrder::getAssigneeId, bo.getAssigneeIdIn());
// 添加数据权限控制
addDataPermissionFilter(lqw);
@@ -171,14 +172,14 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
*
* @param lqw 查询条件构造器
*/
private void addDataPermissionFilter(LambdaQueryWrapper<PmRepairOrder> lqw) {
private void addDataPermissionFilter(LambdaQueryWrapper<HqPmRepairOrder> lqw) {
try {
// 获取当前登录用户信息
var loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
// 未登录用户不能查看任何数据
log.warn("未登录用户尝试访问报修订单数据");
lqw.eq(PmRepairOrder::getId, 0L);
lqw.eq(HqPmRepairOrder::getId, 0L);
return;
}
@@ -210,16 +211,16 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
if (rolePermissions != null && rolePermissions.contains("maintenance_worker")) {
log.debug("当前用户[{}]为维修人员,只能查看自己创建或分配给自己的报修单", currentUserId);
lqw.and(wrapper -> wrapper
.eq(PmRepairOrder::getReporterId, currentUserId) // 自己创建的报修单
.eq(HqPmRepairOrder::getReporterId, currentUserId) // 自己创建的报修单
.or()
.eq(PmRepairOrder::getAssigneeId, currentUserId) // 分配给自己的报修单
.eq(HqPmRepairOrder::getAssigneeId, currentUserId) // 分配给自己的报修单
);
return;
}
// 其他用户:只能看见自己创建的报修单
log.debug("当前用户[{}]为普通用户,只能查看自己创建的报修单", currentUserId);
lqw.eq(PmRepairOrder::getReporterId, currentUserId);
lqw.eq(HqPmRepairOrder::getReporterId, currentUserId);
} catch (Exception e) {
log.error("数据权限过滤异常: {}", e.getMessage(), e);
@@ -228,14 +229,14 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
Long currentUserId = LoginHelper.getUserId();
if (currentUserId != null) {
log.warn("权限过滤异常,回退到只查看自己创建的报修单,用户ID: {}", currentUserId);
lqw.eq(PmRepairOrder::getReporterId, currentUserId);
lqw.eq(HqPmRepairOrder::getReporterId, currentUserId);
} else {
log.error("无法获取当前用户ID,拒绝所有数据访问");
lqw.eq(PmRepairOrder::getId, 0L);
lqw.eq(HqPmRepairOrder::getId, 0L);
}
} catch (Exception ex) {
log.error("权限过滤回退逻辑也失败了: {}", ex.getMessage(), ex);
lqw.eq(PmRepairOrder::getId, 0L);
lqw.eq(HqPmRepairOrder::getId, 0L);
}
}
}
@@ -247,8 +248,8 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(PmRepairOrderBo bo) {
PmRepairOrder add = MapstructUtils.convert(bo, PmRepairOrder.class);
public Boolean insertByBo(HqPmRepairOrderBo bo) {
HqPmRepairOrder add = MapstructUtils.convert(bo, HqPmRepairOrder.class);
validEntityBeforeSave(add);
// 生成工单编号
@@ -289,7 +290,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
*
* @param order 报修工单
*/
private void fillReporterInfo(PmRepairOrder order) {
private void fillReporterInfo(HqPmRepairOrder order) {
try {
// 获取当前登录用户信息
Long userId = LoginHelper.getUserId();
@@ -335,8 +336,8 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(PmRepairOrderBo bo) {
PmRepairOrder update = MapstructUtils.convert(bo, PmRepairOrder.class);
public Boolean updateByBo(HqPmRepairOrderBo bo) {
HqPmRepairOrder update = MapstructUtils.convert(bo, HqPmRepairOrder.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
@@ -344,13 +345,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(PmRepairOrder entity) {
private void validEntityBeforeSave(HqPmRepairOrder entity) {
// 校验工单编号唯一性
if (StringUtils.isNotBlank(entity.getOrderNo())) {
LambdaQueryWrapper<PmRepairOrder> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairOrder::getOrderNo, entity.getOrderNo());
LambdaQueryWrapper<HqPmRepairOrder> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairOrder::getOrderNo, entity.getOrderNo());
if (entity.getId() != null) {
lqw.ne(PmRepairOrder::getId, entity.getId());
lqw.ne(HqPmRepairOrder::getId, entity.getId());
}
boolean exists = baseMapper.exists(lqw);
if (exists) {
@@ -371,7 +372,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
if (isValid) {
// 校验是否可以删除
for (Long id : ids) {
PmRepairOrder order = baseMapper.selectById(id);
HqPmRepairOrder order = baseMapper.selectById(id);
if (order != null && !"1".equals(order.getOrderStatus()) && !"9".equals(order.getOrderStatus())) {
throw new ServiceException("只能删除待分派或已取消状态的工单");
}
@@ -390,13 +391,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
*/
@Override
public Boolean assignOrder(Long orderId, Long assigneeId, String assignRemark) {
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限操作该工单");
}
@@ -427,7 +428,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
}
// 更新工单信息
PmRepairOrder update = new PmRepairOrder();
HqPmRepairOrder update = new HqPmRepairOrder();
update.setId(orderId);
update.setAssigneeId(assigneeId);
update.setAssigneeName(assigneeName); // 设置维修员姓名
@@ -453,13 +454,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
*/
@Override
public Boolean startRepair(Long orderId) {
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限操作该工单");
}
@@ -468,7 +469,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
throw new ServiceException("只能开始已分派状态的工单");
}
PmRepairOrder update = new PmRepairOrder();
HqPmRepairOrder update = new HqPmRepairOrder();
update.setId(orderId);
update.setStartTime(new Date());
update.setOrderStatus("3"); // 进行中
@@ -486,13 +487,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
*/
@Override
public Boolean finishRepair(Long orderId, String repairResult, String usedMaterials) {
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限操作该工单");
}
@@ -510,7 +511,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
totalDuration = (int) (diff / 60000); // 转换为分钟
}
PmRepairOrder update = new PmRepairOrder();
HqPmRepairOrder update = new HqPmRepairOrder();
update.setId(orderId);
update.setFinishTime(finishTime);
update.setTotalDuration(totalDuration);
@@ -530,13 +531,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
*/
@Override
public Boolean cancelOrder(Long orderId, String cancelReason) {
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限操作该工单");
}
@@ -546,7 +547,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
throw new ServiceException("已完成、已评价或已归档的工单不能取消");
}
PmRepairOrder update = new PmRepairOrder();
HqPmRepairOrder update = new HqPmRepairOrder();
update.setId(orderId);
update.setCancelReason(cancelReason);
update.setOrderStatus("9"); // 已取消
@@ -565,12 +566,12 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
String prefix = "RO" + dateStr;
// 查询当天最大序号
LambdaQueryWrapper<PmRepairOrder> lqw = Wrappers.lambdaQuery();
lqw.likeRight(PmRepairOrder::getOrderNo, prefix);
lqw.orderByDesc(PmRepairOrder::getOrderNo);
LambdaQueryWrapper<HqPmRepairOrder> lqw = Wrappers.lambdaQuery();
lqw.likeRight(HqPmRepairOrder::getOrderNo, prefix);
lqw.orderByDesc(HqPmRepairOrder::getOrderNo);
lqw.last("LIMIT 1");
PmRepairOrder lastOrder = baseMapper.selectOne(lqw);
HqPmRepairOrder lastOrder = baseMapper.selectOne(lqw);
int sequence = 1;
if (lastOrder != null && StringUtils.isNotBlank(lastOrder.getOrderNo())) {
@@ -611,13 +612,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
@Override
public Boolean updateProgress(Long orderId, java.util.Map<String, Object> progressData) {
// 1. 验证工单是否存在
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 2. 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限操作该工单");
}
@@ -635,7 +636,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
String currentUserName = loginUser.getUsername();
// 5. 创建进度记录
PmRepairProgress progress = new PmRepairProgress();
HqPmRepairProgress progress = new HqPmRepairProgress();
progress.setOrderId(orderId);
progress.setProgressType(String.valueOf(progressData.get("progressType")));
progress.setProgressStatus(String.valueOf(progressData.get("progressStatus")));
@@ -675,7 +676,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
boolean statusUpdated = false;
if ("2".equals(order.getOrderStatus())) {
// 如果是首次更新进度(从已分派状态),自动将状态改为进行中
PmRepairOrder statusUpdate = new PmRepairOrder();
HqPmRepairOrder statusUpdate = new HqPmRepairOrder();
statusUpdate.setId(orderId);
statusUpdate.setStartTime(new Date());
statusUpdate.setOrderStatus("3"); // 进行中
@@ -707,27 +708,27 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
@Override
public List<java.util.Map<String, Object>> getRepairProgress(Long orderId) {
// 1. 验证工单是否存在
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 2. 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限查看该工单进度");
}
// 3. 查询进度记录
LambdaQueryWrapper<PmRepairProgress> lqw = Wrappers.lambdaQuery();
lqw.eq(PmRepairProgress::getOrderId, orderId);
lqw.orderByAsc(PmRepairProgress::getOperationTime); // 按时间正序排列
LambdaQueryWrapper<HqPmRepairProgress> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmRepairProgress::getOrderId, orderId);
lqw.orderByAsc(HqPmRepairProgress::getOperationTime); // 按时间正序排列
List<PmRepairProgress> progressList = repairProgressMapper.selectList(lqw);
List<HqPmRepairProgress> progressList = repairProgressMapper.selectList(lqw);
// 4. 转换为Map格式返回
List<java.util.Map<String, Object>> result = new java.util.ArrayList<>();
for (PmRepairProgress progress : progressList) {
for (HqPmRepairProgress progress : progressList) {
java.util.Map<String, Object> map = new java.util.HashMap<>();
map.put("id", progress.getId());
map.put("orderId", progress.getOrderId());
@@ -758,12 +759,12 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
private void updateProgressCount(Long orderId) {
try {
// 查询该工单的进度记录总数
LambdaQueryWrapper<PmRepairProgress> countLqw = Wrappers.lambdaQuery();
countLqw.eq(PmRepairProgress::getOrderId, orderId);
LambdaQueryWrapper<HqPmRepairProgress> countLqw = Wrappers.lambdaQuery();
countLqw.eq(HqPmRepairProgress::getOrderId, orderId);
long progressCount = repairProgressMapper.selectCount(countLqw);
// 更新工单的进度计数
PmRepairOrder update = new PmRepairOrder();
HqPmRepairOrder update = new HqPmRepairOrder();
update.setId(orderId);
update.setProgressCount((int) progressCount);
baseMapper.updateById(update);
@@ -815,13 +816,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
@Override
public Boolean completeOrder(Long orderId, String repairResult, String usedMaterials, String followSuggestions) {
// 1. 验证工单是否存在
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 2. 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限操作该工单");
}
@@ -841,7 +842,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
}
// 5. 更新工单状态和完成信息
PmRepairOrder update = new PmRepairOrder();
HqPmRepairOrder update = new HqPmRepairOrder();
update.setId(orderId);
update.setFinishTime(finishTime);
update.setTotalDuration(totalDuration);
@@ -858,7 +859,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
// 6. 添加完成进度记录
try {
var loginUser = LoginHelper.getLoginUser();
PmRepairProgress completionProgress = new PmRepairProgress();
HqPmRepairProgress completionProgress = new HqPmRepairProgress();
completionProgress.setOrderId(orderId);
completionProgress.setProgressType("6"); // 完成维修
completionProgress.setProgressStatus("维修已完成");
@@ -902,13 +903,13 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
Long orderId = Long.valueOf(evaluationData.get("orderId").toString());
// 2. 验证工单是否存在
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 3. 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限评价该工单");
}
@@ -919,8 +920,8 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
}
// 5. 验证是否已经评价过
LambdaQueryWrapper<PmServiceEvaluation> checkLqw = Wrappers.lambdaQuery();
checkLqw.eq(PmServiceEvaluation::getOrderId, orderId);
LambdaQueryWrapper<HqPmServiceEvaluation> checkLqw = Wrappers.lambdaQuery();
checkLqw.eq(HqPmServiceEvaluation::getOrderId, orderId);
boolean hasEvaluated = serviceEvaluationMapper.exists(checkLqw);
if (hasEvaluated) {
throw new ServiceException("该工单已经评价过,不能重复评价");
@@ -933,7 +934,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
// 7. 创建服务评价记录
Date evaluationTime = new Date();
PmServiceEvaluation evaluation = new PmServiceEvaluation();
HqPmServiceEvaluation evaluation = new HqPmServiceEvaluation();
evaluation.setOrderId(orderId);
evaluation.setEvaluatorId(currentUserId);
evaluation.setEvaluatorName(currentUserName);
@@ -989,7 +990,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
}
// 9. 更新工单状态为"已评价",并设置评价得分和评价时间
PmRepairOrder updateOrder = new PmRepairOrder();
HqPmRepairOrder updateOrder = new HqPmRepairOrder();
updateOrder.setId(orderId);
updateOrder.setOrderStatus("5"); // 已评价
updateOrder.setEvaluationScore(evaluation.getOverallScore());
@@ -1002,7 +1003,7 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
// 10. 添加评价进度记录
try {
PmRepairProgress evaluationProgress = new PmRepairProgress();
HqPmRepairProgress evaluationProgress = new HqPmRepairProgress();
evaluationProgress.setOrderId(orderId);
evaluationProgress.setProgressType("7"); // 用户评价
evaluationProgress.setProgressStatus("用户已评价");
@@ -1043,24 +1044,24 @@ public class PmRepairOrderServiceImpl implements IPmRepairOrderService {
public java.util.Map<String, Object> getServiceEvaluation(Long orderId) {
try {
// 1. 验证工单是否存在
PmRepairOrder order = baseMapper.selectById(orderId);
HqPmRepairOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new ServiceException("工单不存在");
}
// 2. 数据权限验证
PmRepairOrderVo orderVo = MapstructUtils.convert(order, PmRepairOrderVo.class);
HqPmRepairOrderVo orderVo = MapstructUtils.convert(order, HqPmRepairOrderVo.class);
if (!hasDataPermission(orderVo)) {
throw new ServiceException("无权限查看该工单的评价");
}
// 3. 查询评价记录
LambdaQueryWrapper<PmServiceEvaluation> lqw = Wrappers.lambdaQuery();
lqw.eq(PmServiceEvaluation::getOrderId, orderId);
lqw.orderByDesc(PmServiceEvaluation::getCreateTime);
LambdaQueryWrapper<HqPmServiceEvaluation> lqw = Wrappers.lambdaQuery();
lqw.eq(HqPmServiceEvaluation::getOrderId, orderId);
lqw.orderByDesc(HqPmServiceEvaluation::getCreateTime);
lqw.last("LIMIT 1");
PmServiceEvaluation evaluation = serviceEvaluationMapper.selectOne(lqw);
HqPmServiceEvaluation evaluation = serviceEvaluationMapper.selectOne(lqw);
if (evaluation == null) {
throw new ServiceException("该工单还没有评价记录");
}
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.hq.mapper.PmRepairAttachmentMapper">
<mapper namespace="org.dromara.hq.mapper.HqPmRepairAttachmentMapper">
<resultMap type="org.dromara.hq.domain.PmRepairAttachment" id="PmRepairAttachmentResult">
<resultMap type="org.dromara.hq.domain.HqPmRepairAttachment" id="HqPmRepairAttachmentResult">
<result property="id" column="id" />
<result property="orderId" column="order_id" />
<result property="fileName" column="file_name" />
@@ -30,8 +30,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="deleted" column="deleted" />
</resultMap>
<sql id="selectPmRepairAttachmentVo">
select id, order_id, file_name, file_path, file_size, file_type, file_extension, file_mime_type, upload_stage, uploader_id, uploader_name, thumbnail_path, duration, resolution, description, sort_order, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted from pm_repair_attachment
<sql id="selectHqPmRepairAttachmentVo">
select id, order_id, file_name, file_path, file_size, file_type, file_extension, file_mime_type, upload_stage, uploader_id, uploader_name, thumbnail_path, duration, resolution, description, sort_order, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted from hq_pm_repair_attachment
</sql>
</mapper>
@@ -2,6 +2,6 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.hq.mapper.PmRepairCategoryMapper">
<mapper namespace="org.dromara.hq.mapper.HqPmRepairCategoryMapper">
</mapper>
@@ -2,7 +2,7 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.hq.mapper.PmRepairOrderMapper">
<mapper namespace="org.dromara.hq.mapper.HqPmRepairOrderMapper">
<!-- 查询拥有"维修人员"角色的用户列表 -->
<select id="selectMaintenanceWorkers" resultType="java.util.Map">
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.hq.mapper.PmRepairProgressMapper">
<mapper namespace="org.dromara.hq.mapper.HqPmRepairProgressMapper">
<resultMap type="org.dromara.hq.domain.PmRepairProgress" id="PmRepairProgressResult">
<resultMap type="org.dromara.hq.domain.HqPmRepairProgress" id="HqPmRepairProgressResult">
<result property="id" column="id" />
<result property="orderId" column="order_id" />
<result property="progressType" column="progress_type" />
@@ -32,8 +32,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="deleted" column="deleted" />
</resultMap>
<sql id="selectPmRepairProgressVo">
select id, order_id, progress_type, progress_status, progress_detail, operator_id, operator_name, operator_role, location_info, gps_latitude, gps_longitude, operation_time, estimated_duration, attachment_ids, next_step, issues_encountered, materials_needed, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted from pm_repair_progress
<sql id="selectHqPmRepairProgressVo">
select id, order_id, progress_type, progress_status, progress_detail, operator_id, operator_name, operator_role, location_info, gps_latitude, gps_longitude, operation_time, estimated_duration, attachment_ids, next_step, issues_encountered, materials_needed, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted from hq_pm_repair_progress
</sql>
</mapper>
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.hq.mapper.HqPmServiceEvaluationMapper">
<resultMap type="org.dromara.hq.domain.HqPmServiceEvaluation" id="HqPmServiceEvaluationResult">
<result property="id" column="id" />
<result property="orderId" column="order_id" />
<result property="evaluatorId" column="evaluator_id" />
<result property="evaluatorName" column="evaluator_name" />
<result property="evaluatedWorkerId" column="evaluated_worker_id" />
<result property="evaluatedWorkerName" column="evaluated_worker_name" />
<result property="overallScore" column="overall_score" />
<result property="qualityScore" column="quality_score" />
<result property="attitudeScore" column="attitude_score" />
<result property="responseSpeedScore" column="response_speed_score" />
<result property="communicationScore" column="communication_score" />
<result property="evaluationContent" column="evaluation_content" />
<result property="satisfactionLevel" column="satisfaction_level" />
<result property="isAnonymous" column="is_anonymous" />
<result property="isPublic" column="is_public" />
<result property="evaluationTime" column="evaluation_time" />
<result property="serviceStartTime" column="service_start_time" />
<result property="serviceEndTime" column="service_end_time" />
<result property="serviceDuration" column="service_duration" />
<result property="positiveTags" column="positive_tags" />
<result property="negativeTags" column="negative_tags" />
<result property="improvementSuggestions" column="improvement_suggestions" />
<result property="remark" column="remark" />
<result property="tenantId" column="tenant_id" />
<result property="createDept" column="create_dept" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="deleted" column="deleted" />
</resultMap>
<sql id="selectHqPmServiceEvaluationVo">
select id, order_id, evaluator_id, evaluator_name, evaluated_worker_id, evaluated_worker_name, overall_score, quality_score, attitude_score, response_speed_score, communication_score, evaluation_content, satisfaction_level, is_anonymous, is_public, evaluation_time, service_start_time, service_end_time, service_duration, positive_tags, negative_tags, improvement_suggestions, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted from hq_pm_service_evaluation
</sql>
</mapper>
@@ -273,7 +273,7 @@ sequenceDiagram
### 核心实体
#### 报修单表 (pm_repair_order)
#### 报修单表 (hq_pm_repair_order)
| 字段名 | 类型 | 长度 | 非空 | 默认值 | 说明 |
| ------------------ | ------------ | ---- | ---- | ------- | ------------- |
@@ -303,7 +303,7 @@ sequenceDiagram
| update_time | DATETIME | - | Y | - | 更新时间 |
| deleted | TINYINT | - | Y | 0 | 删除标记 |
#### 报修附件表 (pm_repair_attachment)
#### 报修附件表 (hq_pm_repair_attachment)
| 字段名 | 类型 | 长度 | 非空 | 默认值 | 说明 |
| --------------- | -------- | ---- | ---- | ------ | --------- |
@@ -321,7 +321,7 @@ sequenceDiagram
| update_time | DATETIME | - | Y | - | 更新时间 |
| deleted | TINYINT | - | Y | 0 | 删除标记 |
#### 维修进度记录表 (pm_repair_progress)
#### 维修进度记录表 (hq_pm_repair_progress)
| 字段名 | 类型 | 长度 | 非空 | 默认值 | 说明 |
| --------------- | -------- | ---- | ---- | ------ | ------------ |
@@ -341,12 +341,12 @@ sequenceDiagram
```mermaid
erDiagram
PM_REPAIR_ORDER ||--o{ PM_REPAIR_ATTACHMENT : "一对多"
PM_REPAIR_ORDER ||--o{ PM_REPAIR_PROGRESS : "一对多"
PM_REPAIR_ORDER }o--|| SYS_USER : "报修人"
PM_REPAIR_ORDER }o--|| SYS_USER : "维修员"
HQ_PM_REPAIR_ORDER ||--o{ HQ_PM_REPAIR_ATTACHMENT : "一对多"
HQ_PM_REPAIR_ORDER ||--o{ HQ_PM_REPAIR_PROGRESS : "一对多"
HQ_PM_REPAIR_ORDER }o--|| SYS_USER : "报修人"
HQ_PM_REPAIR_ORDER }o--|| SYS_USER : "维修员"
PM_REPAIR_ORDER {
HQ_PM_REPAIR_ORDER {
bigint id PK
varchar order_no UK
varchar title
@@ -357,7 +357,7 @@ erDiagram
datetime create_time
}
PM_REPAIR_ATTACHMENT {
HQ_PM_REPAIR_ATTACHMENT {
bigint id PK
bigint repair_order_id FK
varchar file_name
@@ -365,7 +365,7 @@ erDiagram
varchar file_type
}
PM_REPAIR_PROGRESS {
HQ_PM_REPAIR_PROGRESS {
bigint id PK
bigint repair_order_id FK
varchar status
@@ -610,7 +610,7 @@ src/views/maintenance/
#### 数据库命名规范
- **表名**:pm_repair_order、pm_repair_attachment、pm_repair_progress
- **表名**:hq_pm_repair_order、hq_pm_repair_attachment、hq_pm_repair_progress
- **字段名**:repair_order_id、contact_name、urgency_level
- **索引名**:idx_repair_order_status、idx_repair_order_assignee
@@ -31,40 +31,40 @@
### 🗂️ 表结构清单
| 表名 | 中文名称 | 用途 | 关联关系 |
| ----------------------- | ---------- | ---------------------- | ------------ |
| `pm_repair_order` | 报修工单表 | 存储报修申请的核心信息 | 核心表 |
| `pm_repair_attachment` | 报修附件表 | 存储报修相关附件文件 | 关联报修工单 |
| `pm_repair_progress` | 维修进度表 | 记录维修过程状态变更 | 关联报修工单 |
| `pm_service_evaluation` | 服务评价表 | 存储用户服务质量评价 | 关联报修工单 |
| `pm_repair_category` | 维修类型表 | 报修类型分类配置 | 字典表 |
| `pm_system_config` | 系统配置表 | 模块系统参数配置 | 配置表 |
| --------------------------- | ---------- | ---------------------- | ------------ |
| `hq_pm_repair_order` | 报修工单表 | 存储报修申请的核心信息 | 核心表 |
| `hq_pm_repair_attachment` | 报修附件表 | 存储报修相关附件文件 | 关联报修工单 |
| `hq_pm_repair_progress` | 维修进度表 | 记录维修过程状态变更 | 关联报修工单 |
| `hq_pm_service_evaluation` | 服务评价表 | 存储用户服务质量评价 | 关联报修工单 |
| `hq_pm_repair_category` | 维修类型表 | 报修类型分类配置 | 字典表 |
| `hq_pm_system_config` | 系统配置表 | 模块系统参数配置 | 配置表 |
### 📈 数据流转关系
```mermaid
graph TD
A[用户提交报修] --> B[pm_repair_order 报修工单]
B --> C[pm_repair_attachment 附件]
B --> D[pm_repair_progress 进度]
B --> E[pm_service_evaluation 评价]
F[pm_repair_category 类型] --> B
G[pm_system_config 配置] --> B
A[用户提交报修] --> B[hq_pm_repair_order 报修工单]
B --> C[hq_pm_repair_attachment 附件]
B --> D[hq_pm_repair_progress 进度]
B --> E[hq_pm_service_evaluation 评价]
F[hq_pm_repair_category 类型] --> B
G[hq_pm_system_config 配置] --> B
H[sys_user 系统用户] --> B
H --> D
```
## 🏗️ 详细表结构设计
### 1. 报修工单表 (pm_repair_order)
### 1. 报修工单表 (hq_pm_repair_order)
> **核心业务表**:存储报修申请的所有核心信息,是整个模块的数据中心
```sql
-- ----------------------------
-- Table structure for pm_repair_order
-- Table structure for hq_pm_repair_order
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_order`;
CREATE TABLE `pm_repair_order` (
DROP TABLE IF EXISTS `hq_pm_repair_order`;
CREATE TABLE `hq_pm_repair_order` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -82,7 +82,7 @@ CREATE TABLE `pm_repair_order` (
`repair_location` varchar(200) NOT NULL COMMENT '报修地点',
-- 分类和紧急程度
`category_id` bigint(20) NOT NULL COMMENT '维修类型ID,关联pm_repair_category.id',
`category_id` bigint(20) NOT NULL COMMENT '维修类型ID,关联hq_pm_repair_category.id',
`priority_level` tinyint(1) NOT NULL DEFAULT 3 COMMENT '紧急程度:1-非常紧急,2-紧急,3-一般,4-不紧急,5-可延后',
`expected_finish_time` datetime DEFAULT NULL COMMENT '期望完成时间',
@@ -138,16 +138,16 @@ CREATE TABLE `pm_repair_order` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报修工单表';
```
### 2. 报修附件表 (pm_repair_attachment)
### 2. 报修附件表 (hq_pm_repair_attachment)
> **附件管理表**:存储报修过程中上传的图片、视频等多媒体文件信息
```sql
-- ----------------------------
-- Table structure for pm_repair_attachment
-- Table structure for hq_pm_repair_attachment
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_attachment`;
CREATE TABLE `pm_repair_attachment` (
DROP TABLE IF EXISTS `hq_pm_repair_attachment`;
CREATE TABLE `hq_pm_repair_attachment` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -159,7 +159,7 @@ CREATE TABLE `pm_repair_attachment` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
-- 关联信息
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联pm_repair_order.id',
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联hq_pm_repair_order.id',
-- 文件信息
`file_name` varchar(200) NOT NULL COMMENT '文件原始名称',
@@ -196,16 +196,16 @@ CREATE TABLE `pm_repair_attachment` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报修附件表';
```
### 3. 维修进度表 (pm_repair_progress)
### 3. 维修进度表 (hq_pm_repair_progress)
> **进度跟踪表**:记录维修过程中的每一个状态变更和进度更新
```sql
-- ----------------------------
-- Table structure for pm_repair_progress
-- Table structure for hq_pm_repair_progress
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_progress`;
CREATE TABLE `pm_repair_progress` (
DROP TABLE IF EXISTS `hq_pm_repair_progress`;
CREATE TABLE `hq_pm_repair_progress` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -217,7 +217,7 @@ CREATE TABLE `pm_repair_progress` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
-- 关联信息
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联pm_repair_order.id',
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联hq_pm_repair_order.id',
-- 进度信息
`progress_type` char(1) NOT NULL COMMENT '进度类型:1-工单分派,2-接收确认,3-到达现场,4-开始维修,5-进度更新,6-完成维修,7-用户评价,8-工单归档,9-其他',
@@ -260,16 +260,16 @@ CREATE TABLE `pm_repair_progress` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维修进度表';
```
### 4. 服务评价表 (pm_service_evaluation)
### 4. 服务评价表 (hq_pm_service_evaluation)
> **评价反馈表**:存储用户对维修服务的多维度评价和反馈信息
```sql
-- ----------------------------
-- Table structure for pm_service_evaluation
-- Table structure for hq_pm_service_evaluation
-- ----------------------------
DROP TABLE IF EXISTS `pm_service_evaluation`;
CREATE TABLE `pm_service_evaluation` (
DROP TABLE IF EXISTS `hq_pm_service_evaluation`;
CREATE TABLE `hq_pm_service_evaluation` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -281,7 +281,7 @@ CREATE TABLE `pm_service_evaluation` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
-- 关联信息
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联pm_repair_order.id',
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联hq_pm_repair_order.id',
`evaluator_id` bigint(20) NOT NULL COMMENT '评价人ID,关联sys_user.id',
`evaluator_name` varchar(50) NOT NULL COMMENT '评价人姓名',
`evaluated_worker_id` bigint(20) NOT NULL COMMENT '被评价维修员ID,关联sys_user.id',
@@ -330,16 +330,16 @@ CREATE TABLE `pm_service_evaluation` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务评价表';
```
### 5. 维修类型表 (pm_repair_category)
### 5. 维修类型表 (hq_pm_repair_category)
> **分类字典表**:存储维修类型的分类信息,支持层级结构
```sql
-- ----------------------------
-- Table structure for pm_repair_category
-- Table structure for hq_pm_repair_category
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_category`;
CREATE TABLE `pm_repair_category` (
DROP TABLE IF EXISTS `hq_pm_repair_category`;
CREATE TABLE `hq_pm_repair_category` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -395,16 +395,16 @@ CREATE TABLE `pm_repair_category` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维修类型表';
```
### 6. 系统配置表 (pm_system_config)
### 6. 系统配置表 (hq_pm_system_config)
> **配置管理表**:存储物业报修模块的系统参数和配置信息
```sql
-- ----------------------------
-- Table structure for pm_system_config
-- Table structure for hq_pm_system_config
-- ----------------------------
DROP TABLE IF EXISTS `pm_system_config`;
CREATE TABLE `pm_system_config` (
DROP TABLE IF EXISTS `hq_pm_system_config`;
CREATE TABLE `hq_pm_system_config` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -510,16 +510,16 @@ CREATE TABLE `pm_system_config` (
```sql
-- 报修工单表的核心查询索引
-- 1. 按状态和时间查询(管理员常用)
CREATE INDEX idx_status_time ON pm_repair_order (order_status, create_time);
CREATE INDEX idx_status_time ON hq_pm_repair_order (order_status, create_time);
-- 2. 按维修员查询其工单(维修员常用)
CREATE INDEX idx_assignee_status ON pm_repair_order (assignee_id, order_status);
CREATE INDEX idx_assignee_status ON hq_pm_repair_order (assignee_id, order_status);
-- 3. 按报修人查询(用户查看自己的工单)
CREATE INDEX idx_reporter_time ON pm_repair_order (reporter_id, create_time);
CREATE INDEX idx_reporter_time ON hq_pm_repair_order (reporter_id, create_time);
-- 4. 按紧急程度和分派时间查询(工单分派)
CREATE INDEX idx_priority_assign ON pm_repair_order (priority_level, assign_time);
CREATE INDEX idx_priority_assign ON hq_pm_repair_order (priority_level, assign_time);
```
### 2. 附件查询索引
@@ -527,10 +527,10 @@ CREATE INDEX idx_priority_assign ON pm_repair_order (priority_level, assign_time
```sql
-- 附件表的查询索引
-- 1. 按工单查询附件(最常用)
CREATE INDEX idx_order_stage ON pm_repair_attachment (order_id, upload_stage);
CREATE INDEX idx_order_stage ON hq_pm_repair_attachment (order_id, upload_stage);
-- 2. 按上传者查询附件
CREATE INDEX idx_uploader_time ON pm_repair_attachment (uploader_id, create_time);
CREATE INDEX idx_uploader_time ON hq_pm_repair_attachment (uploader_id, create_time);
```
### 3. 进度查询索引
@@ -538,10 +538,10 @@ CREATE INDEX idx_uploader_time ON pm_repair_attachment (uploader_id, create_time
```sql
-- 进度表的查询索引
-- 1. 按工单查询进度(时间线显示)
CREATE INDEX idx_order_operation_time ON pm_repair_progress (order_id, operation_time);
CREATE INDEX idx_order_operation_time ON hq_pm_repair_progress (order_id, operation_time);
-- 2. 按操作人查询进度
CREATE INDEX idx_operator_time ON pm_repair_progress (operator_id, operation_time);
CREATE INDEX idx_operator_time ON hq_pm_repair_progress (operator_id, operation_time);
```
## 📊 数据存储预估
@@ -549,13 +549,13 @@ CREATE INDEX idx_operator_time ON pm_repair_progress (operator_id, operation_tim
### 表数据量预估
| 表名 | 日增长量 | 年数据量 | 存储空间预估 |
| --------------------- | ---------- | ------------ | ------------ |
| pm_repair_order | 50-100 条 | 2-4 万条 | 50-100MB |
| pm_repair_attachment | 200-400 条 | 8-16 万条 | 100-200MB |
| pm_repair_progress | 300-600 条 | 12-24 万条 | 150-300MB |
| pm_service_evaluation | 40-80 条 | 1.6-3.2 万条 | 20-40MB |
| pm_repair_category | 0-5 条 | 100-200 条 | <1MB |
| pm_system_config | 0-2 条 | 50-100 条 | <1MB |
| --------------------------- | ---------- | ------------ | ------------ |
| hq_pm_repair_order | 50-100 条 | 2-4 万条 | 50-100MB |
| hq_pm_repair_attachment | 200-400 条 | 8-16 万条 | 100-200MB |
| hq_pm_repair_progress | 300-600 条 | 12-24 万条 | 150-300MB |
| hq_pm_service_evaluation | 40-80 条 | 1.6-3.2 万条 | 20-40MB |
| hq_pm_repair_category | 0-5 条 | 100-200 条 | <1MB |
| hq_pm_system_config | 0-2 条 | 50-100 条 | <1MB |
### 存储优化建议
@@ -570,7 +570,7 @@ CREATE INDEX idx_operator_time ON pm_repair_progress (operator_id, operation_tim
```sql
-- 插入基础维修类型分类
INSERT INTO `pm_repair_category` (
INSERT INTO `hq_pm_repair_category` (
`id`, `tenant_id`, `category_name`, `category_code`, `category_description`,
`parent_id`, `level_depth`, `required_skills`, `skill_level`,
`estimated_duration`, `icon_url`, `color_code`, `sort_order`, `status`,
@@ -588,7 +588,7 @@ INSERT INTO `pm_repair_category` (
```sql
-- 插入系统配置参数
INSERT INTO `pm_system_config` (
INSERT INTO `hq_pm_system_config` (
`id`, `tenant_id`, `config_key`, `config_value`, `config_type`, `config_group`,
`config_name`, `config_description`, `default_value`, `is_system`, `is_editable`,
`sort_order`, `status`, `create_time`, `create_by`
@@ -285,6 +285,7 @@ private Date updateTime;
**文档版本**: v1.0
**下次评审**: 根据实际使用情况优化
<<<<<<< HEAD
---
# 代码调整记录 - 2025-01-09 - wuzilong
@@ -619,3 +620,450 @@ private Date updateTime;
**📝 维护人员**: wuzilong
**📅 更新时间**: 2025-01-09 23:50:00
=======
## 报告厅预约时间冲突检查功能优化
### 变更时间
2025-01-09 14:30:00
### 变更内容
1. 新增后端时间冲突检查接口
- 添加 `/hq/hallReservation/checkTimeConflict` 接口
- 实现精确的时间冲突检查逻辑
- 支持排除当前预约记录的冲突检查
2. 前端代码优化
- 将时间冲突检查逻辑从前端移至后端
- 添加相关API调用和类型定义
- 优化界面显示效果
3. API文档更新
- 在场地预约管理API文档中添加时间冲突检查接口说明
- 完善接口参数和响应说明
- 添加使用示例
### 涉及文件
1. 后端文件:
- `HallReservationController.java`
- `IHallReservationService.java`
- `HallReservationServiceImpl.java`
- `HallReservationTimeCheckDTO.java`
2. 前端文件:
- `web/src/api/hq/hallAudit/index.ts`
- `web/src/api/hq/hallAudit/types.ts`
- `web/src/views/hq/hallAudit/index.vue`
3. 文档文件:
- `docs/开发文档/API/场地预约管理API.md`
### 变更原因
1. 提高时间冲突检查的准确性
2. 优化代码结构,将业务逻辑移至后端
3. 提升系统性能和可维护性
### 测试验证
1. 接口测试
- [x] 正常时间段检查
- [x] 边界时间检查
- [x] 排除当前记录的检查
- [x] 错误参数处理
2. 前端功能测试
- [x] 审核建议显示
- [x] 时间冲突提示
- [x] 界面交互体验
### 注意事项
1. 时间格式统一使用:yyyy-MM-dd HH:mm:ss
2. 注意处理时区问题
3. 确保前端传参格式正确
### 后续计划
1. 添加批量时间冲突检查功能
2. 优化冲突提示的展示方式
3. 考虑添加预约时间段可视化展示
### 相关文档
- [场地预约管理API文档](../开发文档/API/场地预约管理API.md)
- [报告厅预约系统设计说明](../需求文档/后勤报告厅预约需求/简化版报告厅预约系统设计说明.md)
# 代码调整记录 2025-01-09
## 报告厅预约时间冲突检查功能迁移
### 变更时间
2025-01-09 15:30:00
### 变更内容
1. 代码迁移
- 将代码从 `com.ruoyi.hq` 迁移到 `org.dromara.hq`
- 按照新的项目规范重构代码结构
- 完善实体类和VO类的定义
2. 功能优化
- 优化时间冲突检查逻辑
- 完善参数校验
- 规范化异常处理
- 添加详细的注释说明
3. 规范遵循
- 遵循新的包结构规范
- 实现标准的实体类继承关系
- 完善API文档
### 涉及文件
1. 后端文件:
- `org.dromara.hq.domain.HallReservation`
- `org.dromara.hq.domain.vo.HallReservationVO`
- `org.dromara.hq.domain.dto.HallReservationTimeCheckDTO`
- `org.dromara.hq.mapper.HallReservationMapper`
- `org.dromara.hq.service.IHallReservationService`
- `org.dromara.hq.service.impl.HallReservationServiceImpl`
- `org.dromara.hq.controller.HallReservationController`
2. 文档文件:
- `docs/开发文档/API/场地预约管理API.md`
- `docs/变更管理/code-adjust-record-2025-01-09-wuzilong.md`
### 变更原因
1. 遵循新的项目规范要求
2. 优化代码结构和可维护性
3. 统一项目包命名规范
### 测试验证
1. 功能测试
- [x] 时间冲突检查
- [x] 参数校验
- [x] 异常处理
2. 规范检查
- [x] 包结构符合规范
- [x] 类名和方法名符合规范
- [x] 注释完整规范
### 遗留问题
1. Excel导出相关的注解依赖需要确认
2. 部分Mapper方法可能需要优化
### 后续计划
1. 完善Excel导出功能
2. 优化查询性能
3. 添加缓存支持
### 备注
代码迁移后进行了全面的功能测试,确保所有功能正常运行。如有问题及时反馈。
# 代码调整记录 2025-01-09
## 报告厅预约模块代码规范优化
### 1. 类名规范调整
- 添加模块前缀 `Hq`
- 将 `DTO` 改为 `Bo`
- 涉及文件:
- `HallReservationTimeCheckDTO.java` → `HqHallReservationTimeCheckBo.java`
- 相关的Service、Controller等引用
### 2. 代码结构优化
- 创建标准的VO类:`HallReservationTimeCheckVO`
- 优化Mapper接口和XML映射文件
- 完善了时间冲突检查的业务逻辑
### 3. 前端代码同步更新
- 更新API接口定义
- 添加TypeScript类型声明
- 优化接口返回值处理
### 4. 文档更新
- 更新API文档
- 添加变更记录
## 遗留问题
- [ ] 需要进行完整的功能测试
- [ ] 可能需要补充单元测试用例
## 后续计划
- [ ] 继续优化其他模块的代码规范
- [ ] 完善测试覆盖率
## 变更人
- 作者:wuzilong
- 日期:2025-01-09
- 时间:15:30:00
# 代码调整记录 - 2025-01-09 - wuzilong
## 📋 本日工作总览
**工作重点**: 为报告厅审核建议功能添加后端接口支持
**工作时间**: 2025-01-09 14:00:00 - 17:00:00
**参与人员**: wuzilong
---
## 🚀 主要变更内容
### 1. 📝 VO类基础字段规范修复
**时间**: 2025-01-09 16:00:00
**文件**: `backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/domain/vo/HqHallReservationVo.java`
**变更内容**:
- ✅ 添加缺失的基础字段(符合强制性规范)
- ✅ 包含createBy、createTime、updateBy、updateTime四个字段
- ✅ 时间字段使用`@JsonFormat`注解格式化
- ✅ 创建时间字段支持Excel导出
**技术细节**:
```java
// 添加的基础字段
private Long createBy; // 创建者
private Date createTime; // 创建时间
private Long updateBy; // 更新者
private Date updateTime; // 更新时间
```
**符合规范**: `.cursor/cursorrules-backend.md` → "VO类基础字段规范(强制性)"
### 2. 🆕 新增业务对象类
**时间**: 2025-01-09 16:15:00
#### A. 时间冲突检查BO类
**文件**: `backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/domain/bo/HqHallTimeCheckBo.java`
**功能**: 接收前端时间冲突检查请求参数
- ✅ 报告厅ID(必填)
- ✅ 开始时间/结束时间(必填,JSON格式化)
- ✅ 排除预约ID(可选,用于编辑时排除自身)
- ✅ 完整的参数验证注解
#### B. 时间冲突检查结果VO类
**文件**: `backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/domain/vo/HqHallTimeCheckVo.java`
**功能**: 返回时间冲突检查结果
- ✅ 是否存在冲突(Boolean)
- ✅ 冲突预约列表(List<HqHallReservationVo>)
- ✅ 冲突描述信息(String)
- ✅ 静态工厂方法:noConflict()、hasConflict()
### 3. 🔧 服务层接口扩展
**时间**: 2025-01-09 16:25:00
#### A. Service接口
**文件**: `backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/service/IHqHallReservationService.java`
**新增方法**:
```java
/**
* 检查报告厅时间冲突
* @param timeCheckBo 时间检查参数
* @return 时间冲突检查结果
*/
HqHallTimeCheckVo checkTimeConflict(HqHallTimeCheckBo timeCheckBo);
```
#### B. Service实现类
**文件**: `backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/service/impl/HqHallReservationServiceImpl.java`
**核心业务逻辑**:
- ✅ 智能时间重叠检测算法
- ✅ 只检查已批准状态(status='1')的预约
- ✅ 支持排除指定预约ID(编辑场景)
- ✅ 详细的日志记录
- ✅ 构建标准化返回结果
**时间冲突判定规则**:
```sql
-- 时间重叠条件:
-- startTime < existingEndTime AND endTime > existingStartTime
lqw.and(wrapper -> wrapper
.lt(HqHallReservation::getStartTime, timeCheckBo.getEndTime())
.gt(HqHallReservation::getEndTime, timeCheckBo.getStartTime())
);
```
### 4. 🌐 Controller层API接口
**时间**: 2025-01-09 16:35:00
**文件**: `backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/controller/HqHallReservationController.java`
**新增接口**:
```java
/**
* 检查报告厅时间冲突
*/
@SaCheckPermission("hq:hallReservation:query")
@PostMapping("/checkTimeConflict")
public R<HqHallTimeCheckVo> checkTimeConflict(@Validated @RequestBody HqHallTimeCheckBo timeCheckBo)
```
**接口特点**:
- ✅ 使用现有查询权限(hq:hallReservation:query)
- ✅ POST方式,支持复杂参数传递
- ✅ 完整的参数验证(@Validated)
- ✅ 标准R类型响应格式
### 5. 🔐 权限配置管理
**时间**: 2025-01-09 16:40:00
**文件**: `docs/设计文档/database/hall-audit-permission.sql`
**权限配置内容**:
- ✅ 添加报告厅审核权限(hq:hallReservation:audit)
- ✅ 为admin角色分配审核权限
- ✅ 使用UPSERT逻辑,支持重复执行
- ✅ 包含验证和查询SQL
**权限ID**: 12216 - 报告厅预约审核权限
### 6. 📚 API文档完善
**时间**: 2025-01-09 16:50:00
#### A. 新建专门API文档
**文件**: `docs/开发文档/API/报告厅预约审核API.md`
**文档特点**:
- ✅ 完整的接口说明(400+行)
- ✅ 详细的请求/响应示例
- ✅ 时间冲突检查算法说明
- ✅ 权限配置说明
- ✅ 开发建议和常见问题
- ✅ 完整的版本记录
**核心章节**:
- 🔍 时间冲突检查API
- ✅ 报告厅预约审核API
- 📊 数据字典和权限配置
- 💡 开发建议和问题排查
#### B. 更新API文档总览
**文件**: `docs/开发文档/API/API文档总览.md`
**更新内容**:
- ✅ 添加报告厅预约审核API到分模块文档表
- ✅ 快速导航增加报告厅审核相关链接
- ✅ 完善API文档架构说明
---
## 📊 技术实现亮点
### 1. 🧠 智能时间冲突检测
- **精确算法**: 使用数学逻辑准确判定时间段重叠
- **状态过滤**: 只检查已批准的预约,避免误判
- **灵活排除**: 支持编辑场景下排除自身记录
- **性能优化**: 数据库层面进行时间比较,减少数据传输
### 2. 📋 标准化VO类设计
- **规范遵循**: 严格按照cursorrules-backend.md强制性规范
- **字段完整**: 包含所有必需的基础字段
- **JSON格式化**: 时间字段统一格式化
- **兼容性保证**: 支持MyBatis-Plus自动查询和自定义SQL
### 3. 🔒 完整权限控制
- **接口级权限**: 每个API都有对应权限验证
- **权限复用**: checkTimeConflict复用查询权限
- **SQL配置**: 提供完整的权限配置脚本
- **角色分配**: 自动为admin角色分配权限
### 4. 📖 文档质量保证
- **技术详细**: 包含算法说明和实现细节
- **示例完整**: 提供真实的请求/响应示例
- **问题预防**: 包含常见问题和解决方案
- **版本管理**: 完整的版本记录和变更说明
---
## 🔧 文件变更统计
### 新增文件(4个)
1. `HqHallTimeCheckBo.java` - 时间冲突检查业务对象
2. `HqHallTimeCheckVo.java` - 时间冲突检查结果视图对象
3. `hall-audit-permission.sql` - 权限配置SQL脚本
4. `报告厅预约审核API.md` - API接口文档
### 修改文件(5个)
1. `HqHallReservationVo.java` - 添加基础字段
2. `IHqHallReservationService.java` - 新增服务方法
3. `HqHallReservationServiceImpl.java` - 实现时间冲突检查
4. `HqHallReservationController.java` - 新增API接口
5. `API文档总览.md` - 更新文档导航
### 总代码行数
- **Java代码**: 约150行
- **SQL脚本**: 约35行
- **API文档**: 约400行
- **总计**: 约585行
---
## ✅ 测试验证要求
### 1. 🔧 后端接口测试
- [ ] 时间冲突检查接口功能测试
- [ ] 不同时间段的冲突检测验证
- [ ] 排除ID参数的正确性验证
- [ ] 权限验证测试
### 2. 🌐 前端集成测试
- [ ] 前端API调用测试
- [ ] 审核建议UI显示测试
- [ ] 错误处理和用户提示测试
### 3. 🔐 权限配置验证
- [ ] 执行权限配置SQL脚本
- [ ] 验证admin角色权限分配
- [ ] 测试接口权限控制
---
## 🎯 后续工作计划
### 1. 📱 前端功能优化
- 优化审核建议UI展示
- 添加实时冲突检查
- 完善错误提示机制
### 2. 🧪 测试覆盖
- 编写单元测试
- 完善集成测试
- 添加性能测试
### 3. 📊 监控和日志
- 添加接口调用监控
- 完善错误日志记录
- 性能指标收集
---
## 📝 开发体验总结
### 1. ✅ 成功经验
- **规范遵循**: 严格按照项目规范开发,保证代码质量
- **文档同步**: 代码完成后立即更新文档,保持一致性
- **模块化设计**: 清晰的分层架构,便于维护和扩展
- **权限控制**: 完整的权限体系,保证系统安全
### 2. 💡 改进建议
- **代码生成**: 可以考虑为BO/VO类创建代码生成模板
- **单元测试**: 应该在开发过程中同步编写单元测试
- **性能测试**: 大数据量下的时间冲突检查性能需要验证
### 3. 🎯 质量保证
- **代码审查**: 建议进行代码审查,确保质量
- **文档校验**: API文档示例需要与实际接口保持一致
- **权限测试**: 权限配置需要在实际环境中验证
---
**📝 记录人**: wuzilong
**🕐 记录时间**: 2025-01-09 17:00:00
**🎯 工作质量**: 高质量完成,符合项目规范
**📋 下次重点**: 前端集成测试和功能优化
>>>>>>> 3f9838e8f119e5bd49ded7fde2a6229e312d9a14
@@ -1,321 +1,244 @@
# 代码调整记录 - 2025-01-23 - wuzilong
# 代码调整记录 - 2025-01-23
## 📋 本次变更概述
## 📋 变更概述
**变更类型**: 🏗️ 架构重构 - 后端模块工程统一管理
**变更时间**: 2025-01-23 15:00:00
**影响范围**: 后端模块架构、开发规范、工程结构
**变更日期**: 2025-01-23
**操作人员**: wuzilong
**变更类型**: 数据库表名规范化调整
**影响模块**: 物业报修管理模块
**优先级**: 高
## 🎯 变更背景
## 🎯 变更目标
基于ruoyi-hq工程的成功实践,为了规范化管理后端业务模块,建立统一的工程管理标准,便于后续各业务模块的开发和维护。
将物业报修管理模块的数据库表名从 `pm_*` 格式调整为 `hq_pm_*` 格式,符合项目数据库表名规范要求(后勤管理模块使用 `hq_` 前缀)。
## 🔧 变更详情
## 📊 变更详情
### 1. 📋 后端开发规范更新
### 1. SQL文件调整
**文件**: `.cursor/cursorrules-backend.md`
**新增内容**:
- ✅ **模块工程管理规范(强制性)**
- ✅ **模块命名和位置规范**
- ✅ **Maven配置标准模板**
- ✅ **包结构规范标准**
- ✅ **代码规范要求**
- ✅ **权限命名规范**
- ✅ **模块开发最佳实践**
- ✅ **模块创建检查清单**
**已规划模块列表**:
- ✅ `ruoyi-hq` - 后勤管理模块 (已完成)
- 🚧 `ruoyi-rs` - 人事管理模块
- 🚧 `ruoyi-cw` - 财务管理模块
- 🚧 `ruoyi-aqbw` - 安全保卫管理模块
- 🚧 `ruoyi-zsjy` - 招生就业管理模块
- 🚧 `ruoyi-xg` - 学工管理模块
### 2. 🏗️ 新增业务模块工程
**创建的5个标准业务模块**:
#### 📁 ruoyi-rs (人事管理模块)
```
backend/ruoyi-modules/ruoyi-rs/
├── pom.xml # Maven配置
└── src/main/
├── java/org/dromara/rs/
│ ├── controller/ # 控制器层
│ ├── service/ # 服务接口层
│ │ └── impl/ # 服务实现层
│ ├── mapper/ # 数据访问层
│ └── domain/ # 数据对象层
│ ├── bo/ # 业务对象
│ └── vo/ # 视图对象
└── resources/
└── mapper/ # MyBatis映射文件
```
#### 📁 ruoyi-cw (财务管理模块)
```
backend/ruoyi-modules/ruoyi-cw/
├── pom.xml # Maven配置
└── src/main/
├── java/org/dromara/cw/
│ ├── controller/ # 控制器层
│ ├── service/ # 服务接口层
│ │ └── impl/ # 服务实现层
│ ├── mapper/ # 数据访问层
│ └── domain/ # 数据对象层
│ ├── bo/ # 业务对象
│ └── vo/ # 视图对象
└── resources/
└── mapper/ # MyBatis映射文件
```
#### 📁 ruoyi-aqbw (安全保卫管理模块)
```
backend/ruoyi-modules/ruoyi-aqbw/
├── pom.xml # Maven配置
└── src/main/
├── java/org/dromara/aqbw/
│ ├── controller/ # 控制器层
│ ├── service/ # 服务接口层
│ │ └── impl/ # 服务实现层
│ ├── mapper/ # 数据访问层
│ └── domain/ # 数据对象层
│ ├── bo/ # 业务对象
│ └── vo/ # 视图对象
└── resources/
└── mapper/ # MyBatis映射文件
```
#### 📁 ruoyi-zsjy (招生就业管理模块)
```
backend/ruoyi-modules/ruoyi-zsjy/
├── pom.xml # Maven配置
└── src/main/
├── java/org/dromara/zsjy/
│ ├── controller/ # 控制器层
│ ├── service/ # 服务接口层
│ │ └── impl/ # 服务实现层
│ ├── mapper/ # 数据访问层
│ └── domain/ # 数据对象层
│ ├── bo/ # 业务对象
│ └── vo/ # 视图对象
└── resources/
└── mapper/ # MyBatis映射文件
```
#### 📁 ruoyi-xg (学工管理模块)
```
backend/ruoyi-modules/ruoyi-xg/
├── pom.xml # Maven配置
└── src/main/
├── java/org/dromara/xg/
│ ├── controller/ # 控制器层
│ ├── service/ # 服务接口层
│ │ └── impl/ # 服务实现层
│ ├── mapper/ # 数据访问层
│ └── domain/ # 数据对象层
│ ├── bo/ # 业务对象
│ └── vo/ # 视图对象
└── resources/
└── mapper/ # MyBatis映射文件
```
### 3. ⚙️ Maven配置更新
**文件**: `backend/ruoyi-modules/pom.xml`
**文件**: `docs/设计文档/database/07-property-maintenance-module.sql`
**变更内容**:
```xml
<modules>
<module>ruoyi-demo</module>
<module>ruoyi-generator</module>
<module>ruoyi-job</module>
<module>ruoyi-system</module>
<module>ruoyi-workflow</module>
<module>ruoyi-hq</module>
+ <module>ruoyi-rs</module>
+ <module>ruoyi-cw</module>
+ <module>ruoyi-aqbw</module>
+ <module>ruoyi-zsjy</module>
+ <module>ruoyi-xg</module>
</modules>
- 将所有表名从 `pm_*` 调整为 `hq_pm_*`
- 更新相关注释和关联引用
- 版本号更新至 V1.0.2
**具体表名变更**:
| 原表名 | 新表名 |
|--------|--------|
| `pm_repair_order` | `hq_pm_repair_order` |
| `pm_repair_attachment` | `hq_pm_repair_attachment` |
| `pm_repair_progress` | `hq_pm_repair_progress` |
| `pm_service_evaluation` | `hq_pm_service_evaluation` |
| `pm_repair_category` | `hq_pm_repair_category` |
| `pm_system_config` | `hq_pm_system_config` |
### 2. 数据库重命名脚本
**新增文件**: `docs/设计文档/database/08-property-maintenance-table-rename.sql`
**功能**:
- 智能检测现有表是否存在
- 批量重命名所有物业报修相关表
- 提供验证结果显示
- 支持幂等操作
### 3. 规范文档更新
**更新文件**:
- `docs/业务模块/property-maintenance/技术设计/数据库设计.md`
- `docs/业务模块/property-maintenance/README.md`
**变更内容**:
- 更新所有表名引用
- 修正实体关系图
- 更新数据流转图
### 4. 后端代码调整
**Entity实体类更新**:
- `PmRepairOrder.java` - @TableName("hq_pm_repair_order")
- `PmRepairAttachment.java` - @TableName("hq_pm_repair_attachment")
- `PmRepairProgress.java` - @TableName("hq_pm_repair_progress")
- `PmServiceEvaluation.java` - @TableName("hq_pm_service_evaluation")
- `PmRepairCategory.java` - @TableName("hq_pm_repair_category")
**Mapper XML文件更新**:
- `PmRepairAttachmentMapper.xml` - selectVo中的表名更新
- `PmRepairProgressMapper.xml` - selectVo中的表名更新
- `PmServiceEvaluationMapper.xml` - 新创建完整的Mapper XML
**VO类和BO类完善**:
- 新增:`PmRepairProgressVo.java`、`PmServiceEvaluationVo.java`
- 新增:`PmRepairProgressBo.java`、`PmServiceEvaluationBo.java`
- 更新:所有VO/BO类中的注释表名引用
**Service接口完善**:
- 新增:`IPmRepairProgressService.java`
- 新增:`IPmServiceEvaluationService.java`
**涉及文件**:
```
backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/
├── domain/
│ ├── PmRepairOrder.java ✅
│ ├── PmRepairAttachment.java ✅
│ ├── PmRepairProgress.java ✅
│ ├── PmServiceEvaluation.java ✅
│ ├── PmRepairCategory.java ✅
│ ├── vo/
│ │ ├── PmRepairProgressVo.java ✅ (新增)
│ │ ├── PmServiceEvaluationVo.java ✅ (新增)
│ │ ├── PmRepairOrderVo.java ✅ (注释更新)
│ │ ├── PmRepairAttachmentVo.java ✅ (注释更新)
│ │ └── PmRepairCategoryVo.java ✅ (注释更新)
│ └── bo/
│ ├── PmRepairProgressBo.java ✅ (新增)
│ ├── PmServiceEvaluationBo.java ✅ (新增)
│ ├── PmRepairOrderBo.java ✅ (注释更新)
│ ├── PmRepairAttachmentBo.java ✅ (注释更新)
│ └── PmRepairCategoryBo.java ✅ (注释更新)
├── service/
│ ├── IPmRepairProgressService.java ✅ (新增)
│ └── IPmServiceEvaluationService.java ✅ (新增)
└── resources/mapper/
├── PmRepairAttachmentMapper.xml ✅ (表名更新)
├── PmRepairProgressMapper.xml ✅ (表名更新)
└── PmServiceEvaluationMapper.xml ✅ (新增)
```
### 4. 📂 目录结构说明文件
## 🛠️ 执行步骤
**为每个模块的各层级目录添加了.gitkeep文件**,包含详细的目录用途说明:
- controller/ - REST API控制器层
- service/ - 业务服务接口层
- service/impl/ - 业务服务实现层
- mapper/ - 数据访问层
- domain/ - 实体类
- domain/bo/ - 业务对象
- domain/vo/ - 视图对象
- resources/mapper/ - MyBatis映射文件
### 步骤1: SQL文件调整 ✅
- [x] 更新表结构定义
- [x] 更新关联引用
- [x] 更新索引定义
- [x] 更新初始化数据
## 🎯 模块工程标准化规范
### 步骤2: 数据库表重命名 🔄
- [x] 创建重命名脚本
- [ ] 执行数据库重命名(需手动执行)
### 📋 统一的Maven依赖配置
所有新模块都采用统一的依赖配置,包括:
- **通用工具**: core、doc、mybatis、log、security、web、excel、idempotent、tenant、social、ratelimiter、mail
- **系统模块**: system、job
- **扩展模块**: generator、demo、workflow
### 步骤3: 文档更新 ✅
- [x] 数据库设计文档
- [x] 模块总览文档
- [x] 实体关系图
### 🏛️ 统一的包结构规范
- **包名格式**: `org.dromara.{模块简称}`
- **分层架构**: controller → service → mapper → domain
- **对象分离**: Entity(实体) + BO(业务对象) + VO(视图对象)
### 步骤4: 代码调整 ✅
- [x] Entity类@TableName注解
- [x] 注释中的表名引用
- [x] Mapper XML文件表名更新
- [x] 创建缺失的VO和BO类
- [x] 创建缺失的Service接口
- [x] 创建缺失的Mapper XML文件
### 🔐 统一的权限命名规范
- **权限编码格式**: `{模块简称}:{功能}:{操作}`
- **示例**:
- `rs:employee:list` - 人事模块员工列表查询权限
- `cw:budget:add` - 财务模块预算新增权限
## 📋 验证清单
## ✅ 验证和测试
### 数据库层面
- [ ] 确认旧表名不存在
- [ ] 确认新表名存在且结构正确
- [ ] 确认索引重建成功
- [ ] 确认数据完整性
### 🔧 编译验证
- ✅ 所有新模块Maven配置正确
- ✅ 父模块pom.xml更新成功
- ✅ 目录结构符合标准规范
### 代码层面
- [x] Entity类编译通过
- [x] Mapper XML语法正确
- [x] VO/BO类创建完成
- [x] Service接口定义完成
- [ ] Service实现类创建(待完成)
- [ ] Controller类创建(待完成)
- [ ] 单元测试执行成功
- [ ] API接口调用正常
- [ ] 前端页面显示正常
### 📁 文件结构验证
- ✅ 5个新模块目录结构完整
- ✅ 每个目录都有说明文件
- ✅ 包结构符合ruoyi-hq标准
### 文档层面
- [x] 所有文档表名更新完成
- [x] 图表和示例代码正确
- [x] 变更记录完整
## 🎯 后续计划
## ⚠️ 注意事项
### 🚧 短期计划 (1-2周)
1. **业务需求梳理**: 为每个模块梳理详细的业务需求
2. **数据库设计**: 设计各模块的数据表结构
3. **API设计**: 制定标准的API接口规范
### 数据库操作
1. **备份重要性**: 执行表重命名前务必备份数据库
2. **停机时间**: 重命名操作需要停止相关服务
3. **权限验证**: 确保有足够的数据库操作权限
### 📋 中期计划 (1个月)
1. **核心功能开发**: 优先开发人事管理(rs)和财务管理(cw)模块的核心功能
2. **权限配置**: 完善各模块的菜单和权限配置
3. **测试用例**: 编写完整的测试用例
### 代码部署
1. **同步部署**: 数据库重命名与代码部署需同步进行
2. **回滚预案**: 准备回滚脚本以防意外情况
3. **测试验证**: 部署后需全面测试业务功能
### 🎯 长期计划 (3个月)
1. **功能完善**: 完成所有5个模块的核心功能开发
2. **系统集成**: 各模块之间的数据交互和业务流程打通
3. **性能优化**: 系统性能优化和并发处理能力提升
### 数据一致性
1. **外键约束**: 确认没有外键约束冲突
2. **应用层关联**: 验证应用层关联逻辑正确
3. **缓存刷新**: 清理相关缓存数据
## 📝 注意事项
## 🔧 执行命令
### ⚠️ 开发规范
1. **严格遵循**: 必须按照`.cursor/cursorrules-backend.md`中的模块工程管理规范进行开发
2. **代码质量**: 所有代码必须符合Alibaba Java开发手册规范
3. **文档同步**: 每次代码变更必须同步更新相关文档
### 数据库重命名
```bash
# 连接到MySQL数据库
mysql -h 47.99.164.66 -P 3306 -u root -p hzdyjs_xg
### 🔐 权限管理
1. **权限前缀**: 每个模块的权限前缀不能冲突
2. **菜单配置**: 需要在数据库中配置对应的菜单和权限
3. **接口权限**: 每个API接口必须添加@SaCheckPermission注解
# 执行重命名脚本
source docs/设计文档/database/08-property-maintenance-table-rename.sql;
```
### 📊 数据库规范
1. **基础字段**: 必须包含统一的基础字段(id、tenant_id、create_by等)
2. **逻辑删除**: 统一使用逻辑删除,禁止物理删除
3. **多租户**: 所有业务表必须支持多租户
### 代码重新编译
```bash
# 后端重新编译
cd backend
mvn clean compile
```
## 🏆 变更价值
## 📈 影响评估
### 📈 技术价值
1. **架构统一**: 建立了标准的模块工程架构,便于后续开发和维护
2. **开发效率**: 标准化的结构可以大幅提升新模块的开发效率
3. **代码质量**: 统一的规范保证了代码质量和一致性
### 正面影响
1. **规范统一**: 符合项目数据库命名规范
2. **维护性提升**: 表名更清晰易理解
3. **模块区分**: 清晰区分后勤管理模块表
### 🎯 业务价值
1. **功能扩展**: 为后续各业务模块的开发提供了坚实的基础
2. **系统完整性**: 逐步完善校园管理系统的各个业务模块
3. **运维便利**: 统一的架构降低了系统运维的复杂度
### 风险评估
1. **低风险**: 仅表名变更,表结构不变
2. **影响范围**: 仅限物业报修模块
3. **恢复时间**: 如有问题可快速回滚
### 👥 团队价值
1. **开发规范**: 为团队建立了统一的开发标准和最佳实践
2. **知识沉淀**: 将ruoyi-hq的成功经验规范化并推广到其他模块
3. **协作效率**: 标准化的结构提升了团队协作效率
## ✅ 验证结果
## 🔄 相关文件变更
### 文件变更统计
- SQL文件: 2个(1个更新,1个新增)
- 文档文件: 2个更新
- Entity类: 5个更新(@TableName注解和注释)
- Mapper XML: 3个(2个更新,1个新增)
- VO类: 7个(2个新增,5个注释更新)
- BO类: 7个(2个新增,5个注释更新)
- Service接口: 2个新增
- 总变更行数: 约800行
### 📝 新增文件
1. `.cursor/cursorrules-backend.md` - 新增模块工程管理规范
2. `backend/ruoyi-modules/ruoyi-rs/` - 人事管理模块完整工程
3. `backend/ruoyi-modules/ruoyi-cw/` - 财务管理模块完整工程
4. `backend/ruoyi-modules/ruoyi-aqbw/` - 安全保卫管理模块完整工程
5. `backend/ruoyi-modules/ruoyi-zsjy/` - 招生就业管理模块完整工程
6. `backend/ruoyi-modules/ruoyi-xg/` - 学工管理模块完整工程
### 测试状态
- [ ] 本地环境测试
- [ ] 开发环境验证
- [ ] 功能回归测试
### ✏️ 修改文件
1. `backend/ruoyi-modules/pom.xml` - 添加5个新模块到模块列表
## 📝 后续任务
### 📋 新增前端文件(2025-01-23 15:30:00更新)
1. **数据库执行**: 在测试环境执行表重命名脚本
2. **完善代码层**:
- 创建 `PmRepairProgressServiceImpl.java` 和 `PmServiceEvaluationServiceImpl.java`
- 创建 `PmRepairProgressController.java` 和 `PmServiceEvaluationController.java`
- 完善Mapper接口方法
3. **代码测试**: 验证所有API接口正常工作
4. **前端联调**: 确认前端页面数据显示正常
5. **文档同步**: 更新相关API文档和使用说明
**前端界面文件夹** (web/src/views/):
- ✅ `web/src/views/rs/` - 人事管理界面文件夹 + README.md
- ✅ `web/src/views/cw/` - 财务管理界面文件夹 + README.md
- ✅ `web/src/views/xg/` - 学工管理界面文件夹 + README.md
- ✅ `web/src/views/zsjy/` - 招生就业管理界面文件夹 + README.md
- ✅ `web/src/views/aqbw/` - 安全保卫管理界面文件夹 + README.md
## 🔗 相关文档
**前端API文件夹** (web/src/api/):
- ✅ `web/src/api/rs/` - 人事管理API文件夹 + index.ts + README.md
- ✅ `web/src/api/cw/` - 财务管理API文件夹 + index.ts + README.md
- ✅ `web/src/api/xg/` - 学工管理API文件夹 + index.ts + README.md
- ✅ `web/src/api/zsjy/` - 招生就业管理API文件夹 + index.ts + README.md
- ✅ `web/src/api/aqbw/` - 安全保卫管理API文件夹 + index.ts + README.md
### 📋 影响评估
- **编译构建**: ✅ 正常,新模块已正确集成到Maven构建流程
- **运行时**: ✅ 正常,新模块目前为空工程,不影响现有功能
- **依赖关系**: ✅ 正常,所有依赖配置正确
- **前端构建**: ✅ 正常,新增的TypeScript文件不会影响编译
## 🔄 数据库表命名规范补充更新 (2025-01-23 16:30:00)
### 📋 变更背景
为了配合新创建的5个业务模块,需要建立统一的数据库表命名规范,确保不同模块的数据库表有清晰的命名区分,便于后续开发和维护。
### 🔴 新增表命名规范(强制性)
**文件**: `.cursor/cursorrules-backend.md`
**新增内容**: 在数据库规范章节添加了完整的表命名规范
**模块表名前缀规范**:
- **后勤管理**:`hq_` 开头 (如:`hq_repair`, `hq_maintenance`)
- **人事管理**:`rs_` 开头 (如:`rs_employee`, `rs_department`)
- **财务管理**:`cw_` 开头 (如:`cw_budget`, `cw_expense`)
- **学工管理**:`xg_` 开头 (如:`xg_student`, `xg_dormitory`)
- **招生就业**:`zsjy_` 开头 (如:`zsjy_application`, `zsjy_job`)
- **安全保卫**:`aqbw_` 开头 (如:`aqbw_security`, `aqbw_patrol`)
- **系统公共**:`common_` 开头 (如:`common_dict`, `common_config`)
### ✅ 规范内容要点
1. **命名示例**: 提供了7个模块的完整CREATE TABLE示例
2. **强制性要求**: 明确了6个强制性命名要求和4个禁止事项
3. **实体类映射**: 提供了表名到Java实体类的命名对应规则
4. **规范位置**: 放置在数据库规范的强制性核心规范之后,基础字段模板之前
### 🔄 后勤管理模块补充更新 (2025-01-23 16:35:00)
**补充内容**: 添加后勤管理模块的表命名规范
- ✅ **后勤管理**: `hq_` 开头 (如:`hq_repair`, `hq_maintenance`)
- ✅ **命名示例**: 添加了后勤管理模块的CREATE TABLE示例
- ✅ **实体类映射**: 添加了 `hq_repair` → `HqRepair` 的命名对应示例
**更新说明**: 现在表命名规范涵盖完整的7个业务模块,包括已开发的后勤管理模块和5个新规划模块。
### 🎯 规范价值
1. **模块区分**: 清晰区分不同业务模块的数据表
2. **开发效率**: 标准化命名减少开发时的困惑
3. **维护便利**: 便于后续数据库维护和权限管理
4. **团队协作**: 为团队提供统一的数据库命名标准
- [项目数据库命名规范](docs/设计文档/database/mysql-database-standard.md)
- [物业报修模块文档](docs/业务模块/property-maintenance/README.md)
- [数据库设计文档](docs/业务模块/property-maintenance/技术设计/数据库设计.md)
---
**总结**: 本次变更成功建立了前后端模块工程的统一管理规范,创建了5个后端业务模块和对应的前端文件夹结构,并补充完善了包含7个业务模块的完整数据库表命名规范(后勤、人事、财务、学工、招生就业、安全保卫、系统公共),为后续各业务模块的全栈开发奠定了坚实的架构基础。所有变更都经过仔细验证,确保系统稳定性不受影响。
**变更完成时间**: 2025-01-23 18:45:00
**最后更新**: 2025-01-23 18:45:00 - 完成所有表名调整和基础代码结构
**下次更新计划**: 待Service实现类和Controller创建完成后更新验证结果
@@ -0,0 +1,26 @@
# 代码调整记录 2025-07-01
## 报告厅预约界面样式优化
### 调整内容
- 优化了报告厅预约界面中暂无图片占位符的样式
- 使暂无图片占位符宽度与预约用途输入框保持一致(450px)
- 增加了响应式布局支持,在小屏幕上自动适应容器宽度
### 具体修改
- 文件: `web/src/views/hq/hallReservation/index.vue`
- 修改了 `no-images-placeholder` 样式类:
- 将固定宽度改为响应式宽度 `width: 100%`
- 添加最大宽度限制 `max-width: 450px`
- 保持与预约用途输入框视觉统一
### 优化效果
- 提升了界面视觉统一性
- 改善了响应式布局表现
- 优化了用户体验
### 相关文件
- [x] web/src/views/hq/hallReservation/index.vue
提交人: wuzilong
完成时间: 2025-07-01 10:30:00
@@ -346,3 +346,196 @@
- 次要按钮: 使用轮廓式设计
- 危险按钮: 使用错误色
- 禁用状态: 降低透明度
# API接口基础规范(2025-07-01更新)
## 一、系统基础模块规范
### 1. 系统模块代码位置
所有涉及以下功能的API接口代码必须统一写在 `ruoyi-system` 工程中:
- 用户管理相关(SysUser)
- 组织机构相关(SysDept)
- 角色权限相关(SysRole)
- 菜单权限相关(SysMenu)
- 字典管理相关(SysDict)
- 系统配置相关(SysConfig)
- 租户管理相关(SysTenant)
#### 代码组织结构
```
ruoyi-system/
├── src/main/java/org/dromara/system/
│ ├── controller/system/ # 控制器(注意是system子包)
│ ├── service/ # 服务接口
│ ├── service/impl/ # 服务实现
│ ├── mapper/ # 数据访问层
│ └── domain/ # 实体类和VO对象
```
### 2. 租户过滤规范
#### 2.1 Controller层
```java
@RestController
@RequestMapping("/system/xxx")
public class XxxController {
@GetMapping("/list")
public R<List<XxxVO>> list(@RequestParam(value = "tenantId", required = false) String tenantId) {
// 如果tenantId为空,会自动使用当前用户的租户ID
return R.ok(xxxService.selectXxxList(tenantId));
}
}
```
#### 2.2 Service层
```java
@Service
public class XxxServiceImpl implements IXxxService {
@Override
public List<XxxVO> selectXxxList(String tenantId) {
if (StringUtils.isEmpty(tenantId)) {
tenantId = SecurityUtils.getTenantId();
}
return baseMapper.selectXxxList(tenantId);
}
}
```
#### 2.3 Mapper层
```java
@Mapper
public interface XxxMapper {
@Select("""
SELECT
x.*
FROM xxx x
WHERE x.tenant_id = #{tenantId}
AND x.del_flag = '0'
ORDER BY x.create_time DESC
""")
List<Xxx> selectXxxList(@Param("tenantId") String tenantId);
}
```
### 3. 统一返回值规范
#### 3.1 后端返回格式
```java
// 统一返回格式
public class R<T> {
private int code; // 状态码
private String msg; // 消息
private T data; // 数据对象
}
// 分页返回格式
public class TableDataInfo<T> {
private int code; // 状态码
private String msg; // 消息
private List<T> data; // 数据对象
private long total; // 总记录数
}
```
#### 3.2 前端获取数据规范
```typescript
// 标准数据获取
const getList = async () => {
try {
const res = await listApi();
// 正确:从data字段获取数据
dataList.value = res.data || [];
} catch (error) {
console.error('获取数据失败:', error);
dataList.value = [];
}
};
// 分页数据获取
const getPageList = async (query: PageQuery) => {
try {
const res = await pageApi(query);
// 正确:从data字段获取列表数据
dataList.value = res.data || [];
// 获取总数
total.value = res.total || 0;
} catch (error) {
console.error('获取分页数据失败:', error);
dataList.value = [];
total.value = 0;
}
};
```
## 二、代码生成规范
### 1. 代码生成器配置
#### 1.1 系统模块配置
- 当生成的代码涉及用户、组织、角色等基础功能时:
- 包名:`org.dromara.system`
- 模块名:`ruoyi-system`
- 表前缀:`sys_`
#### 1.2 租户字段配置
- 必须包含租户字段:`tenant_id`
- 字段类型:`varchar(20)`
- 默认值:`'000000'`
- 注释:`租户编号`
### 2. 模板调整建议
#### 2.1 Controller模板
```java
@ApiOperation("查询${functionName}列表")
@SaCheckPermission("${permissionPrefix}:list")
@GetMapping("/list")
public R<List<${ClassName}VO>> list(${ClassName}BO bo) {
// 确保设置租户ID
if (StringUtils.isEmpty(bo.getTenantId())) {
bo.setTenantId(SecurityUtils.getTenantId());
}
return R.ok(${className}Service.queryList(bo));
}
```
#### 2.2 Service模板
```java
@Override
public List<${ClassName}VO> queryList(${ClassName}BO bo) {
// 租户过滤
LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery();
lqw.eq(${ClassName}::getTenantId, bo.getTenantId());
// ... 其他查询条件
return baseMapper.selectVoList(lqw);
}
```
## 三、注意事项
### 1. 租户ID处理
- 前端可以选择性传入租户ID
- 如果未传入,后端使用当前用户的租户ID
- 禁止跨租户查询(除非特殊说明)
### 2. 返回值处理
- 前端统一从data字段获取数据
- 禁止使用rows或其他字段名
- 空值处理使用空数组或空对象
### 3. 系统模块开发
- 涉及基础功能必须在ruoyi-system中开发
- 保持代码结构统一
- 遵循权限命名规范
---
**📝 维护说明**:
- 更新时间:2025-07-01
- 更新人:wuzilong
- 基于实际项目开发经验总结
- 持续补充和优化
+8
View File
@@ -20,6 +20,7 @@
| **🛠️ 物业报修管理 API** | [物业报修管理 API.md](./物业报修管理API.md) | 维修分类、报修工单、进度跟踪、服务评价 | 物业报修功能开发 |
| **📊 后勤满意度调查 API** | [后勤满意度调查 API.md](./后勤满意度调查API.md) | 问卷管理、答题、题目管理、答题记录 | 满意度调查功能开发 |
| **🏢 场地预约管理 API** | [场地预约管理 API.md](./场地预约管理API.md) | 场地信息管理、预约申请、审批流程、冲突检查 | 场地预约功能开发 |
| **🎭 报告厅预约审核 API** | [报告厅预约审核 API.md](./报告厅预约审核API.md) | 审核建议生成、时间冲突检查、批量审核处理 | 报告厅审核功能开发 |
### 📝 阅读建议
@@ -62,6 +63,13 @@
- [预约审批](./场地预约管理API.md#审批场地预约) - 审批流程管理
- [时间冲突检查](./场地预约管理API.md#检查时间冲突) - 预约冲突检测
#### 🎭 报告厅审核相关
- [报告厅预约查询](./报告厅预约审核API.md#报告厅预约查询-api) - 预约列表和详情查询
- [时间冲突检查](./报告厅预约审核API.md#时间冲突检查-api) - 智能冲突检测
- [批量审核处理](./报告厅预约审核API.md#报告厅预约审核-api) - 批准/拒绝预约申请
- [审核建议生成](./报告厅预约审核API.md#时间冲突检查-api) - 基于冲突检查的智能建议
### 🔍 按开发阶段查找
#### 🚀 项目初期
@@ -393,6 +393,101 @@
---
## 预约时间冲突检查接口
### 1. 检查报告厅预约时间冲突
#### 接口说明
- 接口名称:检查报告厅预约时间是否存在冲突
- 接口地址:`/hq/hallReservation/checkTimeConflict`
- 请求方式:POST
- 接口说明:根据报告厅ID和预约时间段,检查是否与已批准的预约存在时间冲突
#### 请求参数
```json
{
"hallId": "1", // 报告厅ID
"startTime": "2025-01-15 09:00:00", // 预约开始时间
"endTime": "2025-01-15 12:00:00", // 预约结束时间
"excludeId": "100" // 排除的预约ID(可选,用于编辑时排除自身)
}
```
#### 响应参数
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"hasConflict": true, // 是否存在冲突
"conflictList": [ // 冲突的预约列表
{
"id": "101",
"hallId": "1",
"hallName": "第一报告厅",
"startTime": "2025-01-15 10:00:00",
"endTime": "2025-01-15 13:00:00",
"applicantName": "张三",
"status": "1", // 预约状态:0-审核中,1-已批准,2-已拒绝
"auditTime": "2025-01-14 15:30:00"
}
]
}
}
```
#### 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 500 | 系统错误 |
| 400 | 参数错误 |
#### 请求示例
```http
POST /hq/hallReservation/checkTimeConflict
Content-Type: application/json
{
"hallId": "1",
"startTime": "2025-01-15 09:00:00",
"endTime": "2025-01-15 12:00:00"
}
```
#### 响应示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"hasConflict": true,
"conflictList": [
{
"id": "101",
"hallId": "1",
"hallName": "第一报告厅",
"startTime": "2025-01-15 10:00:00",
"endTime": "2025-01-15 13:00:00",
"applicantName": "张三",
"status": "1",
"auditTime": "2025-01-14 15:30:00"
}
]
}
}
```
#### 注意事项
1. 时间格式必须为:yyyy-MM-dd HH:mm:ss
2. 只检查与已批准(status=1)的预约是否冲突
3. 时间冲突的判定规则:
- 新预约的开始时间在已有预约时间段内
- 新预约的结束时间在已有预约时间段内
- 新预约时间段完全包含已有预约时间段
---
## 数据字典
### 场地类型 (hq_venue_type)
@@ -0,0 +1,429 @@
# 报告厅预约审核 API 文档
## 文档信息
- **文档名称**: 报告厅预约审核 API 文档
- **创建时间**: 2025-01-09
- **维护负责人**: 开发团队
- **最后更新**: 2025-01-09 16:30:00
## 概述
报告厅预约审核模块提供专门的报告厅预约审核功能 API 接口,包括审核建议生成、时间冲突检查、批量审核等核心功能。该模块主要面向管理员用户,用于对用户提交的报告厅预约申请进行审核处理。
### 核心功能
- 📋 **预约列表查询**:查询待审核和已审核的报告厅预约申请
- 🔍 **时间冲突检查**:智能检测预约时间是否与已批准的预约冲突
- 💡 **审核建议生成**:基于时间冲突检查结果自动生成审核建议
- ✅ **批量审核处理**:支持批准/拒绝单个或多个预约申请
- 📊 **审核记录管理**:记录审核人、审核时间、审核备注等详细信息
### 技术特点
- **智能冲突检测**:精确的时间重叠算法,避免预约冲突
- **批量操作支持**:高效处理多个预约的审核操作
- **权限控制严格**:完整的权限验证机制
- **日志记录完整**:详细的操作日志和审核轨迹
---
## 🔐 基础规范
### 请求头规范
所有需要认证的接口都必须在请求头中携带:
```json
{
"Authorization": "Bearer {access_token}",
"clientid": "e5cd7e4891bf95d1d19206ce24a7b32e",
"Content-Type": "application/json;charset=utf-8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"referer": "http://localhost/login?redirect=%252Findex"
}
```
### 响应格式规范
- **分页列表**: TableDataInfo 格式 (total, rows, code, msg)
- **非分页接口**: R 类型格式 (code, msg, data)
- **错误响应**: 统一错误格式
详细规范请参考:[API文档基础规范](./API文档基础规范.md)
---
## 📋 报告厅预约查询 API
### 1. 查询报告厅预约列表
**接口描述**: 查询报告厅预约申请列表,支持多种条件筛选
**请求方式**: `GET`
**请求路径**: `/hq/hallReservation/list`
**权限要求**: `hq:hallReservation:list`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
| ------------- | ------- | ---- | ---------------------------------- | ---------------- |
| hallName | String | 否 | 报告厅名称(模糊查询) | 第一报告厅 |
| startTime | String | 否 | 预约开始时间(查询该时间之后的) | 2025-01-10 |
| endTime | String | 否 | 预约结束时间(查询该时间之前的) | 2025-01-15 |
| applicantName | String | 否 | 申请人姓名(模糊查询) | 张三 |
| status | String | 否 | 预约状态(0-审核中 1-已批准 2-已拒绝) | 0 |
| pageNum | Integer | 否 | 页码,默认1 | 1 |
| pageSize | Integer | 否 | 每页数量,默认10 | 10 |
#### 响应示例
```json
{
"total": 5,
"rows": [
{
"id": 1234567890123456789,
"hallId": 1,
"hallName": "第一报告厅",
"applicantId": 987654321,
"applicantName": "张三",
"applicantPhone": "13800138000",
"startTime": "2025-01-15 09:00:00",
"endTime": "2025-01-15 12:00:00",
"reservationPurpose": "学术讲座 - 人工智能发展趋势",
"status": "0",
"auditBy": null,
"auditName": null,
"auditTime": null,
"auditRemark": null,
"createTime": "2025-01-09 14:30:00"
}
],
"code": 200,
"msg": "查询成功"
}
```
### 2. 获取报告厅预约详情
**接口描述**: 获取指定预约申请的详细信息
**请求方式**: `GET`
**请求路径**: `/hq/hallReservation/{id}`
**权限要求**: `hq:hallReservation:query`
#### 路径参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
| ------ | ---- | ---- | ------- | ----------------- |
| id | Long | 是 | 预约 ID | 1234567890123456789 |
#### 响应示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1234567890123456789,
"hallId": 1,
"hallName": "第一报告厅",
"applicantId": 987654321,
"applicantName": "张三",
"applicantPhone": "13800138000",
"startTime": "2025-01-15 09:00:00",
"endTime": "2025-01-15 12:00:00",
"reservationPurpose": "学术讲座 - 人工智能发展趋势",
"status": "0",
"auditBy": null,
"auditName": null,
"auditTime": null,
"auditRemark": null,
"createTime": "2025-01-09 14:30:00"
}
}
```
---
## 🔍 时间冲突检查 API
### 检查报告厅预约时间冲突
**接口描述**: 检查指定报告厅在指定时间段内是否存在已批准的预约冲突
**请求方式**: `POST`
**请求路径**: `/hq/hallReservation/checkTimeConflict`
**权限要求**: `hq:hallReservation:query`
#### 请求体
```json
{
"hallId": 1,
"startTime": "2025-01-15 09:00:00",
"endTime": "2025-01-15 12:00:00",
"excludeId": 1234567890123456789
}
```
#### 请求参数说明
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
| --------- | ---- | ---- | --------------------------------------- | --------------------- |
| hallId | Long | 是 | 报告厅ID | 1 |
| startTime | Date | 是 | 预约开始时间(格式:yyyy-MM-dd HH:mm:ss) | 2025-01-15 09:00:00 |
| endTime | Date | 是 | 预约结束时间(格式:yyyy-MM-dd HH:mm:ss) | 2025-01-15 12:00:00 |
| excludeId | Long | 否 | 排除的预约ID(编辑时排除自己) | 1234567890123456789 |
#### 响应示例
**无冲突时:**
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"hasConflict": false,
"conflictList": [],
"conflictMessage": "无时间冲突"
}
}
```
**有冲突时:**
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"hasConflict": true,
"conflictList": [
{
"id": 1234567890123456790,
"hallId": 1,
"hallName": "第一报告厅",
"applicantName": "李四",
"applicantPhone": "13900139000",
"startTime": "2025-01-15 10:00:00",
"endTime": "2025-01-15 13:00:00",
"reservationPurpose": "企业宣讲会",
"status": "1",
"auditName": "管理员",
"auditTime": "2025-01-14 15:30:00"
}
],
"conflictMessage": "发现 1 个时间冲突的已批准预约"
}
}
```
#### 时间冲突判定规则
两个时间段存在冲突的条件:
1. 新预约开始时间 < 已存在预约结束时间
2. 新预约结束时间 > 已存在预约开始时间
即:`startTime < existingEndTime AND endTime > existingStartTime`
#### 冲突检查逻辑
1. **同一报告厅**:只检查相同报告厅ID的预约
2. **已批准状态**:只检查状态为"1"(已批准)的预约
3. **排除自身**:修改预约时可以通过excludeId排除自身
4. **精确匹配**:使用数据库时间比较,确保检查准确性
---
## ✅ 报告厅预约审核 API
### 批量审核报告厅预约
**接口描述**: 批量批准或拒绝报告厅预约申请
**请求方式**: `PUT`
**请求路径**: `/hq/hallReservation/audit`
**权限要求**: `hq:hallReservation:audit`
#### 请求体
```json
{
"ids": [1234567890123456789, 1234567890123456790],
"status": "1",
"auditRemark": "审核通过,请按时使用报告厅"
}
```
#### 请求参数说明
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
| ----------- | ---------- | ---- | ------------------------------ | ----------------------------------------------- |
| ids | Long[] | 是 | 要审核的预约ID数组 | [1234567890123456789, 1234567890123456790] |
| status | String | 是 | 审核状态(1-批准 2-拒绝) | 1 |
| auditRemark | String | 否 | 审核备注(拒绝时建议填写原因) | 审核通过,请按时使用报告厅 |
#### 响应示例
**成功时:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
**失败时:**
```json
{
"code": 500,
"msg": "审核失败,请稍后重试"
}
```
#### 审核业务逻辑
1. **状态检查**:只有状态为"0"(审核中)的预约可以被审核
2. **权限验证**:必须具有`hq:hallReservation:audit`权限
3. **审核信息记录**:
- auditBy:当前登录用户ID
- auditName:当前登录用户姓名
- auditTime:当前系统时间
- auditRemark:审核备注
4. **批量操作**:支持一次审核多个预约,保证操作的原子性
#### 审核状态说明
| 状态值 | 状态名称 | 说明 |
| ------ | -------- | ---------------------- |
| 0 | 审核中 | 用户提交,等待审核 |
| 1 | 已批准 | 管理员审核通过 |
| 2 | 已拒绝 | 管理员审核拒绝 |
---
## 📊 数据字典
### 预约状态 (hall_reserv_status)
| 字典值 | 字典标签 | 说明 | 颜色标识 |
| ------ | -------- | ------------ | -------- |
| 0 | 审核中 | 待管理员审核 | 橙色 |
| 1 | 已批准 | 审核通过 | 绿色 |
| 2 | 已拒绝 | 审核拒绝 | 红色 |
---
## 🔒 权限配置
### 接口权限要求
| 接口 | 权限标识 | 说明 |
| ----------------------------- | --------------------------- | -------------- |
| 查询报告厅预约列表 | hq:hallReservation:list | 列表查询权限 |
| 获取报告厅预约详情 | hq:hallReservation:query | 详情查询权限 |
| 检查报告厅预约时间冲突 | hq:hallReservation:query | 详情查询权限 |
| 批量审核报告厅预约 | hq:hallReservation:audit | 审核操作权限 |
### 权限配置SQL
参考文件:`docs/设计文档/database/hall-audit-permission.sql`
---
## 💡 开发建议
### 前端集成建议
1. **审核建议显示**:
- 调用时间冲突检查接口
- 根据返回结果显示审核建议
- 冲突时显示警告图标和冲突详情
2. **批量操作优化**:
- 支持全选/反选功能
- 批量操作前进行确认提示
- 操作结果统一反馈
3. **用户体验优化**:
- 实时冲突检查
- 操作loading状态
- 详细的错误提示
### 后端开发注意事项
1. **时间处理**:
- 统一使用数据库时间进行比较
- 注意时区处理
- 精确到秒级别
2. **并发控制**:
- 审核操作加锁机制
- 防止重复审核
- 事务一致性保证
3. **日志记录**:
- 详细的操作日志
- 异常情况记录
- 性能监控
---
## 🐛 常见问题
### Q1: 时间冲突检查不准确怎么办?
**A**: 检查以下几点:
1. 确认时间格式是否正确(yyyy-MM-dd HH:mm:ss)
2. 检查数据库中的时间数据是否准确
3. 确认冲突检查的SQL逻辑是否正确
4. 检查是否正确排除了指定的预约ID
### Q2: 批量审核部分失败怎么处理?
**A**:
1. 后端使用事务确保原子性
2. 如果部分失败,整个操作回滚
3. 返回具体的失败原因
4. 前端提示用户重新操作
### Q3: 审核权限验证失败?
**A**:
1. 检查用户是否具有`hq:hallReservation:audit`权限
2. 确认权限配置SQL是否正确执行
3. 检查角色权限分配是否正确
4. 确认Token是否有效
---
## 📝 版本记录
### v1.0.0 (2025-01-09)
**新增功能**:
- ✨ 报告厅预约时间冲突检查接口
- ✨ 智能审核建议生成功能
- ✨ 批量审核处理功能
- ✨ 完整的权限控制机制
- ✨ 详细的审核记录管理
**技术特点**:
- 🔍 精确的时间重叠检测算法
- 📊 批量操作支持
- 🔒 严格的权限验证
- 📝 完整的操作日志
**API接口**:
- GET `/hq/hallReservation/list` - 查询预约列表
- GET `/hq/hallReservation/{id}` - 获取预约详情
- POST `/hq/hallReservation/checkTimeConflict` - 检查时间冲突
- PUT `/hq/hallReservation/audit` - 批量审核预约
---
**📝 维护人**: 开发团队
**🔄 最后更新**: 2025-01-09 16:30:00
**📁 文件位置**: `docs/开发文档/API/报告厅预约审核API.md`
**🎯 相关文档**: [API文档基础规范](./API文档基础规范.md) | [API文档总览](./API文档总览.md)
+91 -1
View File
@@ -1,4 +1,9 @@
# 用户认证登录 API
# 用户认证登录API文档
## 目录
1. [用户登录](#用户登录)
2. [获取用户信息](#获取用户信息)
3. [获取具有指定权限的用户列表](#获取具有指定权限的用户列表)
## 📋 文档说明
@@ -660,3 +665,88 @@ methods: {
**📝 维护**: 用户认证登录 API 文档
**🔄 更新**: 2025-01-03
**📁 文件**: 用户认证登录 API.md
## 获取具有指定权限的用户列表
### 基本信息
- **接口URL**: `/system/user/list-by-permission`
- **请求方式**: GET
- **接口描述**: 获取具有指定权限的用户列表
- **权限标识**: `system:user:list`
### 请求参数
#### Query参数
| 参数名 | 类型 | 是否必须 | 说明 | 示例值 |
|--------|------|----------|------|--------|
| permission | string | 是 | 权限标识符 | hq:hallAudit:list |
### 响应参数
```typescript
{
code: number; // 状态码
msg: string; // 提示信息
rows: {
userId: string; // 用户ID
userName: string; // 用户名
nickName: string; // 昵称
dept: {
deptId: string; // 部门ID
deptName: string; // 部门名称
};
avatar: string; // 头像URL
}[];
total: number; // 总记录数
}
```
### 响应示例
```json
{
"code": 200,
"msg": "操作成功",
"rows": [
{
"userId": "1",
"userName": "admin",
"nickName": "管理员",
"dept": {
"deptId": "1",
"deptName": "后勤部"
},
"avatar": "http://example.com/avatar.jpg"
}
],
"total": 1
}
```
### 错误码
| 错误码 | 说明 |
|--------|------|
| 401 | 未登录或登录超时 |
| 403 | 没有权限 |
| 500 | 服务器内部错误 |
### 注意事项
1. 该接口需要用户登录后才能访问
2. 返回的用户信息已过滤敏感字段
3. 建议前端对结果进行缓存,避免频繁请求
4. 返回结果按照用户ID升序排序
### 使用示例
```typescript
// 前端调用示例
import { listUsersByPermission } from '@/api/system/user';
async function getAuditors() {
try {
const res = await listUsersByPermission('hq:hallAudit:list');
if (res.code === 200) {
return res.rows;
}
} catch (error) {
console.error('获取审核人员列表失败:', error);
return [];
}
}
```
@@ -0,0 +1,47 @@
# 报告厅信息界面测试执行报告 (2025-07-09)
## ✨ 执行概览
- **执行时间**: 2025-07-09
- **测试环境**: macOS darwin 24.5.0 / Chrome (Playwright MCP 默认浏览器)
- **前端地址**: http://localhost
- **后端地址**: http://localhost:8080
- **登录账号**: admin / admin123
## 📋 关联用例
来源: `报告厅信息界面测试用例-2025-07-09.md`
## 🧪 执行结果一览
| 用例编号 | 用例名称 | 结果 | 备注 |
|----------|----------|------|------|
| TC001 | 页面加载和核心元素检查 | ❌ 失败 | 访问路径 `/hq/hallInfo` 返回 404 页面 |
| TC002-TC012 | 依赖页面正常加载 | ⏸ 未执行 | 前置条件 (TC001) 不通过 |
## ❌ 失败详情 (TC001)
1. **操作步骤回放**
- 访问登录页 `http://localhost`,成功登录 (admin / admin123)。
- 登录后自动跳转到首页 `/index`。
- 调用 `page.goto('http://localhost/hq/hallInfo')`。
- 等待 3 秒后页面展示 **404 错误**,提示“找不到网页”。
2. **预期**: 报告厅信息页在 3 秒内加载并显示核心元素。
3. **实际**: 返回 404,页面不存在。
4. **截图**: (已保存在 Playwright 执行记录)
## 🔍 结论 & 建议
- **路由未注册**: 前端路由 `/hq/hallInfo` 在当前发布版本中未生效,导致 404。
- **菜单/权限缺失**: 若使用动态路由,请确认后台菜单数据已包含 `hq/hallInfo` 且管理员拥有 `hq:hallInfo:query` 权限。
- **接口可用性**: API 文件和 Vue 组件已存在,说明功能开发中;建议前端注册路由并在后台菜单启用后重新执行测试。
## 📌 后续行动
1. 核对后台菜单配置,确认是否已发布“报告厅信息”功能。
2. 确认前端 `router.ts` (或动态路由加载逻辑) 已包含该页面。
3. 功能上线完成后重新执行全部 12 条用例,并生成新的执行报告。
---
> **提示**: 测试执行失败时,应先解决环境/功能就绪问题,再进行自动化测试脚本调试。
@@ -0,0 +1,204 @@
# 报告厅信息界面功能测试用例
## 🎯 测试概览
**编写时间**: 2025-07-09
**测试工具**: Cursor + Playwright MCP
**测试范围**: 后勤管理 → 报告厅信息界面
**测试类型**: 端到端功能 + UI 可用性 + 校验逻辑
**预计执行时长**: 40–60 分钟
## 📋 测试准备
### 1. 环境要求
- ✅ 前端运行地址: `http://localhost` (默认 80 端口)
- ✅ 后端运行地址: `http://localhost:8080`
- ✅ 管理员测试账号: `admin / admin123`
- ✅ Playwright MCP 插件已启用
- ✅ 测试图片目录: `test/hall-info/test-images/`
### 2. 基础测试数据
```json
{
"testHalls": [
{
"hallName": "测试报告厅 A",
"hallDescription": "可容纳 200 人的大型多媒体报告厅",
"hallStatus": "1",
"hallImageFile": "报告厅-01.jpg"
},
{
"hallName": "测试报告厅 B",
"hallDescription": "小型多功能报告厅,适合研讨会",
"hallStatus": "1",
"hallImageFile": "报告厅-02.jpg"
}
]
}
```
## 🧪 测试执行计划
> **Playwright 代码片段**示例均遵循 MCP snapshot → click/type → waitFor 的通用写法,可直接复制到 Playwright 脚本中。
### 阶段一:页面基础验证(10 分钟)
#### TC001: 页面加载和核心元素检查
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证“报告厅信息”页面可以在 3 秒内完成加载并正确渲染核心元素 |
| **前置条件** | 已登录系统,导航到 `后勤管理 → 报告厅管理 → 报告厅信息` 菜单 |
| **步骤** | 1. 调用 `browser.navigate('http://localhost/hq/bgtgl/hallInfo')` <br/>2. `await browser.waitFor(3000)` <br/>3. `const snapshot = await browser.snapshot()` 验证页面包含“报告厅信息”标题、搜索框、【新增】按钮、表格区域、分页组件 |
| **预期结果** | 页面 3 秒内加载完成;所有核心元素可见且可交互 |
| **Playwright 示例** |
```javascript
await browser.navigate('http://localhost/hq/hallInfo');
await browser.waitFor(3000);
const snapshot = await browser.snapshot();
// 断言标题存在
```
#### TC002: 默认数据列表展示
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证表格列(名称、状态、图片、描述、操作)显示完整,数据加载正确 |
| **步骤** | 1. 页面加载完成后解析表格 <br/>2. 检查列头文本 <br/>3. 验证至少存在 1 条数据 |
| **预期结果** | 列头文本正确,数据行显示完整,分页信息正确 |
### 阶段二:CRUD 功能测试(20 分钟)
#### TC003: 新增报告厅信息
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证新增功能完整性,包括表单校验、图片上传、保存成功提示及列表刷新 |
| **前置条件** | None |
| **步骤** | 1. 点击【新增】按钮 <br/>2. 填写“报告厅名称”、“描述” <br/>3. 选择状态为“可用” <br/>4. 上传 任意一张图片 (路径: test/hall-info/test-images/`) <br/>5. 点击【保存】按钮 <br/>6. 等待成功提示并搜索新增名称 |
| **预期结果** | 弹窗关闭、提示“操作成功”,列表第一行显示新数据,图片缩略图显示正常 |
| **Playwright 示例** |
```javascript
await browser.click('新增按钮', "button:has-text('新增')");
await browser.type('名称', "input[placeholder*='报告厅名称']", '测试报告厅 A');
await browser.type('描述', "textarea[placeholder*='描述']", '可容纳 200 人的大型多媒体报告厅');
await browser.click('状态下拉', "select[placeholder*='状态']");
await browser.selectOption('状态选项', 'select', ['1']);
// 上传图片
await browser.fileUpload('选择图片', "input[type='file']", 'test/hall-info/test-images/');
await browser.click('保存', "button:has-text('保存')");
await browser.waitFor(2000);
```
#### TC004: 编辑报告厅信息
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证编辑功能并确保修改后的数据正确保存 |
| **前置条件** | 已存在“测试报告厅 A”记录 |
| **步骤** | 1. 在列表中搜索并点击对应行【编辑】 <br/>2. 修改描述为“可容纳 180–200 人,带高清投影” <br/>3. 点击【保存】 <br/>4. 验证修改生效 |
| **预期结果** | 提示“操作成功”,描述字段显示新内容 |
#### TC005: 状态切换(可用 ↔ 不可用)
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证状态切换按钮或下拉框能实时更新报告厅可用状态 |
| **步骤** | 1. 点击状态切换或下拉,改为“不可用” <br/>2. 等待提示刷新 <br/>3. 再次切换回“可用” |
| **预期结果** | 状态字段值正确切换,表格颜色/标签同步更新 |
#### TC006: 删除报告厅信息
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证单条删除与批量删除功能 |
| **步骤** | 1. 勾选“测试报告厅 A”复选框 <br/>2. 点击【删除】按钮,确认弹窗 <br/>3. 验证提示成功并列表中不再出现该数据 |
| **预期结果** | 数据删除成功,表格刷新后该条目消失 |
### 阶段三:表单校验与异常测试(5 分钟)
#### TC007: 必填字段校验
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证未填写必填项时的校验提示 |
| **步骤** | 1. 点击【新增】 <br/>2. 不填任何字段直接保存 |
| **预期结果** | 名称、状态、图片均显示红色校验提示,无法提交成功 |
#### TC008: 名称重复校验
| 字段 | 说明 |
|------|------|
| **测试目标** | 当报告厅名称已存在时,系统应提示重复并阻止保存 |
| **步骤** | 1. 新增或编辑时填写已存在的名称“测试报告厅 B” <br/>2. 点击保存 |
| **预期结果** | 弹窗提示“报告厅名称已存在”,表单不关闭 |
### 阶段四:搜索、排序与分页(5 分钟)
#### TC009: 关键字搜索
| 字段 | 说明 |
|------|------|
| **步骤** | 1. 在搜索框输入“测试报告厅” <br/>2. 点击【搜索】 |
| **预期结果** | 表格仅显示名称包含“测试报告厅”的记录,分页信息更新 |
#### TC010: 分页跳转
| 字段 | 说明 |
|------|------|
| **步骤** | 1. 若数据超过 10 条,点击分页到第 2 页 <br/>2. 观察页面加载 |
| **预期结果** | 正确跳转到第 2 页,数据加载正常 |
### 阶段五:图片上传专项测试(5 分钟)
#### TC011: 多张图片上传
| 字段 | 说明 |
|------|------|
| **测试目标** | 验证支持一次上传多张图片并正确保存 |
| **步骤** | 1. 新增报告厅时选择 任意多张图片 <br/>2. 保存后查看图片列表 |
| **预期结果** | 所有上传图片均生成缩略图,放大预览正常 |
#### TC012: 不合法格式/大小校验
| 字段 | 说明 |
|------|------|
| **测试目标** | 上传超出大小或非图片格式文件时提示错误 |
| **步骤** | 1. 尝试上传 `test.txt` 或 >5MB 的图片 |
| **预期结果** | 显示“仅支持 jpg/png 且大小 ≤5MB”错误提示,禁止上传 |
### 阶段六:权限与安全(可选)
- TC013: 普通用户无法访问“报告厅信息”菜单
- TC014: Token 失效后访问页面应跳转登录
## ✅ 验证点汇总
- 页面元素存在性与响应速度
- CRUD 全流程成功提示与数据持久化
- 表单前端 & 后端校验一致性
- 图片上传与文件系统存储完整性
- 搜索/分页/排序交互稳定性
- 权限与安全控制
## ⏱️ 性能指标
| 指标 | 目标 |
|------|------|
| 页面首次加载 | ≤ 3 秒 |
| 新增 / 编辑请求 | ≤ 1 秒 |
| 图片上传 | ≤ 2 秒/张 |
## 📂 Playwright 代码与资源路径
- **脚本目录**: `test/hall-info/`
- **示例脚本**: `test/hall-info/hall-info-e2e.spec.js`
- **图片资源**: `test/hall-info/test-images/`
## 📝 备注
1. 以上测试用例可作为 Playwright 自动化脚本直接参考。
2. 图片上传请使用绝对路径或 `path.join(process.env.HOME, 'Downloads/报告厅照片', fileName)` 动态拼接。
3. 测试完成后,请生成执行报告并提交至 `docs/测试文档/后勤管理/报告厅信息/` 目录下,命名为 `报告厅信息界面测试执行报告-YYYY-MM-DD.md`。
@@ -1,10 +1,11 @@
-- ==========================================
-- 物业报修管理模块数据库脚本
-- 版本:V1.0.1
-- 功能:物业报修管理模块完整表结构
-- 版本:V1.0.2
-- 功能:物业报修管理模块完整表结构(表名规范化调整)
-- 作者:Cursor AI 辅助
-- 时间:2025-01-23
-- 依据文档:docs/业务模块/property-maintenance/技术设计/数据库设计.md
-- 表名规范:使用hq_前缀标识后勤管理模块
-- ==========================================
-- 设置字符集
@@ -12,10 +13,10 @@ SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for pm_repair_order
-- Table structure for hq_pm_repair_order
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_order`;
CREATE TABLE `pm_repair_order` (
DROP TABLE IF EXISTS `hq_pm_repair_order`;
CREATE TABLE `hq_pm_repair_order` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -33,7 +34,7 @@ CREATE TABLE `pm_repair_order` (
`repair_location` varchar(200) NOT NULL COMMENT '报修地点',
-- 分类和紧急程度
`category_id` bigint(20) NOT NULL COMMENT '维修类型ID,关联pm_repair_category.id',
`category_id` bigint(20) NOT NULL COMMENT '维修类型ID,关联hq_pm_repair_category.id',
`priority_level` tinyint(1) NOT NULL DEFAULT 3 COMMENT '紧急程度:1-非常紧急,2-紧急,3-一般,4-不紧急,5-可延后',
`expected_finish_time` datetime DEFAULT NULL COMMENT '期望完成时间',
@@ -89,10 +90,10 @@ CREATE TABLE `pm_repair_order` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报修工单表';
-- ----------------------------
-- Table structure for pm_repair_attachment
-- Table structure for hq_pm_repair_attachment
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_attachment`;
CREATE TABLE `pm_repair_attachment` (
DROP TABLE IF EXISTS `hq_pm_repair_attachment`;
CREATE TABLE `hq_pm_repair_attachment` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -104,7 +105,7 @@ CREATE TABLE `pm_repair_attachment` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
-- 关联信息
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联pm_repair_order.id',
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联hq_pm_repair_order.id',
-- 文件信息
`file_name` varchar(200) NOT NULL COMMENT '文件原始名称',
@@ -141,10 +142,10 @@ CREATE TABLE `pm_repair_attachment` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报修附件表';
-- ----------------------------
-- Table structure for pm_repair_progress
-- Table structure for hq_pm_repair_progress
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_progress`;
CREATE TABLE `pm_repair_progress` (
DROP TABLE IF EXISTS `hq_pm_repair_progress`;
CREATE TABLE `hq_pm_repair_progress` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -156,7 +157,7 @@ CREATE TABLE `pm_repair_progress` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
-- 关联信息
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联pm_repair_order.id',
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联hq_pm_repair_order.id',
-- 进度信息
`progress_type` char(1) NOT NULL COMMENT '进度类型:1-工单分派,2-接收确认,3-到达现场,4-开始维修,5-进度更新,6-完成维修,7-用户评价,8-工单归档,9-其他',
@@ -199,10 +200,10 @@ CREATE TABLE `pm_repair_progress` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维修进度表';
-- ----------------------------
-- Table structure for pm_service_evaluation
-- Table structure for hq_pm_service_evaluation
-- ----------------------------
DROP TABLE IF EXISTS `pm_service_evaluation`;
CREATE TABLE `pm_service_evaluation` (
DROP TABLE IF EXISTS `hq_pm_service_evaluation`;
CREATE TABLE `hq_pm_service_evaluation` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -214,7 +215,7 @@ CREATE TABLE `pm_service_evaluation` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
-- 关联信息
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联pm_repair_order.id',
`order_id` bigint(20) NOT NULL COMMENT '报修工单ID,关联hq_pm_repair_order.id',
`evaluator_id` bigint(20) NOT NULL COMMENT '评价人ID,关联sys_user.id',
`evaluator_name` varchar(50) NOT NULL COMMENT '评价人姓名',
`evaluated_worker_id` bigint(20) NOT NULL COMMENT '被评价维修员ID,关联sys_user.id',
@@ -263,10 +264,10 @@ CREATE TABLE `pm_service_evaluation` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务评价表';
-- ----------------------------
-- Table structure for pm_repair_category
-- Table structure for hq_pm_repair_category
-- ----------------------------
DROP TABLE IF EXISTS `pm_repair_category`;
CREATE TABLE `pm_repair_category` (
DROP TABLE IF EXISTS `hq_pm_repair_category`;
CREATE TABLE `hq_pm_repair_category` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -322,10 +323,10 @@ CREATE TABLE `pm_repair_category` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维修类型表';
-- ----------------------------
-- Table structure for pm_system_config
-- Table structure for hq_pm_system_config
-- ----------------------------
DROP TABLE IF EXISTS `pm_system_config`;
CREATE TABLE `pm_system_config` (
DROP TABLE IF EXISTS `hq_pm_system_config`;
CREATE TABLE `hq_pm_system_config` (
-- 标准基础字段(必须)
`id` bigint(20) NOT NULL COMMENT '主键ID',
`tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户编号',
@@ -373,7 +374,7 @@ CREATE TABLE `pm_system_config` (
-- ----------------------------
-- 初始化数据:维修类型分类
-- ----------------------------
INSERT INTO `pm_repair_category` (
INSERT INTO `hq_pm_repair_category` (
`id`, `tenant_id`, `category_name`, `category_code`, `category_description`,
`parent_id`, `level_depth`, `required_skills`, `skill_level`,
`estimated_duration`, `icon_url`, `color_code`, `sort_order`, `status`,
@@ -389,7 +390,7 @@ INSERT INTO `pm_repair_category` (
-- ----------------------------
-- 初始化数据:系统配置参数
-- ----------------------------
INSERT INTO `pm_system_config` (
INSERT INTO `hq_pm_system_config` (
`id`, `tenant_id`, `config_key`, `config_value`, `config_type`, `config_group`,
`config_name`, `config_description`, `default_value`, `is_system`, `is_editable`,
`sort_order`, `status`, `create_time`, `create_by`
@@ -410,16 +411,16 @@ INSERT INTO `pm_system_config` (
-- 创建核心业务查询索引
-- ----------------------------
-- 报修工单表的核心查询索引
CREATE INDEX idx_status_time ON pm_repair_order (order_status, create_time);
CREATE INDEX idx_assignee_status ON pm_repair_order (assignee_id, order_status);
CREATE INDEX idx_reporter_time ON pm_repair_order (reporter_id, create_time);
CREATE INDEX idx_priority_assign ON pm_repair_order (priority_level, assign_time);
CREATE INDEX idx_status_time ON hq_pm_repair_order (order_status, create_time);
CREATE INDEX idx_assignee_status ON hq_pm_repair_order (assignee_id, order_status);
CREATE INDEX idx_reporter_time ON hq_pm_repair_order (reporter_id, create_time);
CREATE INDEX idx_priority_assign ON hq_pm_repair_order (priority_level, assign_time);
-- 附件表的查询索引
CREATE INDEX idx_uploader_time ON pm_repair_attachment (uploader_id, create_time);
CREATE INDEX idx_uploader_time ON hq_pm_repair_attachment (uploader_id, create_time);
-- 进度表的查询索引
CREATE INDEX idx_order_operation_time ON pm_repair_progress (order_id, operation_time);
CREATE INDEX idx_operator_time ON pm_repair_progress (operator_id, operation_time);
CREATE INDEX idx_order_operation_time ON hq_pm_repair_progress (order_id, operation_time);
CREATE INDEX idx_operator_time ON hq_pm_repair_progress (operator_id, operation_time);
SET FOREIGN_KEY_CHECKS = 1;
@@ -0,0 +1,166 @@
-- ==========================================
-- 物业报修管理模块表名重命名脚本
-- 版本:V1.0.0
-- 功能:将现有pm_开头的表名重命名为hq_pm_开头
-- 作者:Cursor AI 辅助
-- 时间:2025-01-23
-- 目的:符合项目表名规范(后勤管理模块使用hq_前缀)
-- ==========================================
-- 设置字符集
SET NAMES utf8mb4;
-- 检查表是否存在并进行重命名
-- 注意:在重命名之前请确保没有其他进程在使用这些表
-- 1. 重命名报修工单表
-- 先检查表是否存在
SELECT 'Checking table existence...' AS step;
-- 重命名主要业务表
-- 1. pm_repair_order -> hq_pm_repair_order
SET @sql = IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_order') > 0,
'RENAME TABLE pm_repair_order TO hq_pm_repair_order',
'SELECT "Table pm_repair_order does not exist" AS info'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 2. pm_repair_attachment -> hq_pm_repair_attachment
SET @sql = IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_attachment') > 0,
'RENAME TABLE pm_repair_attachment TO hq_pm_repair_attachment',
'SELECT "Table pm_repair_attachment does not exist" AS info'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 3. pm_repair_progress -> hq_pm_repair_progress
SET @sql = IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_progress') > 0,
'RENAME TABLE pm_repair_progress TO hq_pm_repair_progress',
'SELECT "Table pm_repair_progress does not exist" AS info'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 4. pm_service_evaluation -> hq_pm_service_evaluation
SET @sql = IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_service_evaluation') > 0,
'RENAME TABLE pm_service_evaluation TO hq_pm_service_evaluation',
'SELECT "Table pm_service_evaluation does not exist" AS info'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 5. pm_repair_category -> hq_pm_repair_category
SET @sql = IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_category') > 0,
'RENAME TABLE pm_repair_category TO hq_pm_repair_category',
'SELECT "Table pm_repair_category does not exist" AS info'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 6. pm_system_config -> hq_pm_system_config
SET @sql = IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_system_config') > 0,
'RENAME TABLE pm_system_config TO hq_pm_system_config',
'SELECT "Table pm_system_config does not exist" AS info'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 验证重命名结果
SELECT 'Verifying table rename results...' AS step;
-- 检查新表名是否存在
SELECT
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hq_pm_repair_order')
THEN 'EXISTS'
ELSE 'NOT EXISTS'
END AS hq_pm_repair_order_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hq_pm_repair_attachment')
THEN 'EXISTS'
ELSE 'NOT EXISTS'
END AS hq_pm_repair_attachment_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hq_pm_repair_progress')
THEN 'EXISTS'
ELSE 'NOT EXISTS'
END AS hq_pm_repair_progress_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hq_pm_service_evaluation')
THEN 'EXISTS'
ELSE 'NOT EXISTS'
END AS hq_pm_service_evaluation_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hq_pm_repair_category')
THEN 'EXISTS'
ELSE 'NOT EXISTS'
END AS hq_pm_repair_category_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'hq_pm_system_config')
THEN 'EXISTS'
ELSE 'NOT EXISTS'
END AS hq_pm_system_config_status;
-- 检查旧表名是否还存在(应该不存在)
SELECT
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_order')
THEN 'STILL EXISTS (ERROR)'
ELSE 'REMOVED (OK)'
END AS old_pm_repair_order_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_attachment')
THEN 'STILL EXISTS (ERROR)'
ELSE 'REMOVED (OK)'
END AS old_pm_repair_attachment_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_progress')
THEN 'STILL EXISTS (ERROR)'
ELSE 'REMOVED (OK)'
END AS old_pm_repair_progress_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_service_evaluation')
THEN 'STILL EXISTS (ERROR)'
ELSE 'REMOVED (OK)'
END AS old_pm_service_evaluation_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_repair_category')
THEN 'STILL EXISTS (ERROR)'
ELSE 'REMOVED (OK)'
END AS old_pm_repair_category_status,
CASE
WHEN EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pm_system_config')
THEN 'STILL EXISTS (ERROR)'
ELSE 'REMOVED (OK)'
END AS old_pm_system_config_status;
SELECT 'Table rename operation completed!' AS result;
@@ -0,0 +1,36 @@
-- ==========================================
-- 报告厅审核权限配置脚本
-- 功能:为报告厅审核功能添加完整权限配置
-- 时间:2025-01-09 16:30:00
-- 说明:使用UPSERT逻辑,支持重复执行
-- ==========================================
-- 第一部分:添加报告厅审核权限到现有菜单下
INSERT INTO sys_menu (
menu_id, menu_name, parent_id, order_num, path, component,
query_param, is_frame, is_cache, menu_type, visible, status,
perms, icon, create_by, create_time, update_by, update_time, remark
)
SELECT
12216, '报告厅预约审核', 2100, 6, '', '', '', 1, 0, 'F', '0', '0',
'hq:hallReservation:audit', '#', 1, now(), 1, now(), '报告厅预约审核权限'
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 12216);
-- 第二部分:为admin角色分配审核权限(如果未分配)
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
SELECT 1, 12216 WHERE NOT EXISTS (
SELECT 1 FROM sys_role_menu WHERE role_id = 1 AND menu_id = 12216
);
-- 第三部分:验证配置结果
SELECT '报告厅审核权限配置验证' as message,
COUNT(*) as new_menu_count,
(SELECT COUNT(*) FROM sys_role_menu WHERE role_id = 1 AND menu_id = 12216) as role_permission_count
FROM sys_menu
WHERE menu_id = 12216;
-- 第四部分:查看所有报告厅相关权限
SELECT menu_id, menu_name, perms, menu_type, parent_id
FROM sys_menu
WHERE perms LIKE 'hq:hall%'
ORDER BY menu_id;
+10 -6
View File
@@ -3,25 +3,30 @@ org.dromara.system.domain.vo.SysNoticeVo
org.dromara.system.domain.bo.SysDictDataBo
org.dromara.system.domain.bo.SysNoticeBo
org.dromara.xg.domain.vo.XgAuditRecordVo
org.dromara.hq.domain.vo.HqPmRepairOrderVo
org.dromara.system.domain.vo.SysClientVo
org.dromara.hq.domain.vo.HqSatSurveyQuestionVo
org.dromara.system.domain.vo.SysConfigVo
org.dromara.hq.domain.bo.HqPmRepairAttachmentBo
org.dromara.hq.domain.bo.HqHallInfoBo
org.dromara.system.domain.vo.SysDeptVo
org.dromara.hq.domain.bo.HqSatSurveyBo
org.dromara.hq.domain.bo.PmRepairOrderBo
org.dromara.system.domain.bo.SysDictTypeBo
org.dromara.system.domain.bo.SysDeptBo
org.dromara.system.domain.vo.SysOssConfigVo
org.dromara.hq.domain.vo.HqSatSurveyVo
org.dromara.hq.domain.vo.HqSatRecordVo
org.dromara.hq.domain.bo.HqHallReservationBo
org.dromara.hq.domain.bo.HqPmServiceEvaluationBo
org.dromara.hq.domain.bo.HqSatRecordBo
org.dromara.system.domain.bo.SysSocialBo
org.dromara.system.domain.bo.SysTenantPackageBo
org.dromara.test.domain.vo.XgStuMsgVo
org.dromara.system.domain.vo.SysDictTypeVo
org.dromara.hq.domain.bo.HqSatQuestionBo
org.dromara.hq.domain.bo.HqPmRepairProgressBo
org.dromara.hq.domain.bo.HqPmRepairOrderBo
org.dromara.hq.domain.vo.HqPmRepairAttachmentVo
org.dromara.system.domain.vo.SysTenantPackageVo
org.dromara.system.domain.vo.SysSocialVo
org.dromara.hq.domain.bo.HqSatCategoryBo
@@ -35,8 +40,9 @@ org.dromara.hq.domain.vo.HqHallInfoVo
org.dromara.system.domain.bo.SysConfigBo
org.dromara.web.domain.vo.TenantListVo
org.dromara.system.domain.bo.SysLogininforBo
org.dromara.hq.domain.bo.PmRepairCategoryBo
org.dromara.hq.domain.vo.HqPmRepairProgressVo
org.dromara.xg.domain.vo.XgStudentInfoVo
org.dromara.hq.domain.vo.HqPmRepairCategoryVo
org.dromara.hq.domain.bo.HqSatOptionBo
org.dromara.system.domain.bo.SysOssBo
org.dromara.system.domain.vo.SysMenuVo
@@ -44,27 +50,25 @@ org.dromara.hq.domain.vo.HqSatCategoryVo
org.dromara.system.domain.vo.SysUserVo
org.dromara.hq.domain.vo.HqVenueInfoVo
org.dromara.xg.domain.vo.XgClassStudentVo
org.dromara.hq.domain.vo.PmRepairCategoryVo
org.dromara.system.domain.bo.SysTenantBo
org.dromara.hq.domain.vo.PmRepairAttachmentVo
org.dromara.system.domain.bo.SysOssConfigBo
org.dromara.system.domain.bo.SysMenuBo
org.dromara.hq.domain.vo.HqVenueReservationVo
org.dromara.system.domain.vo.SysOperLogVo
org.dromara.system.domain.vo.SysLogininforVo
org.dromara.hq.domain.vo.PmRepairOrderVo
org.dromara.hq.domain.bo.HqSatDetailBo
org.dromara.test.domain.bo.XgStuMsgBo
org.dromara.hq.domain.bo.HqVenueInfoBo
org.dromara.system.domain.bo.SysUserBo
org.dromara.system.domain.vo.SysOssVo
org.dromara.hq.domain.bo.HqPmRepairCategoryBo
org.dromara.hq.domain.vo.HqSatOptionVo
org.dromara.hq.domain.vo.HqHallReservationVo
org.dromara.hq.domain.vo.HqPmServiceEvaluationVo
org.dromara.system.domain.vo.SysTenantVo
org.dromara.hq.domain.vo.HqSatQuestionVo
org.dromara.hq.domain.bo.HqSatSurveyQuestionBo
org.dromara.system.domain.vo.SysDictDataVo
org.dromara.hq.domain.bo.PmRepairAttachmentBo
org.dromara.xg.domain.bo.XgClassInfoBo
org.dromara.hq.domain.bo.HqVenueReservationBo
org.dromara.system.domain.bo.SysRoleBo
+28 -75
View File
@@ -1,112 +1,65 @@
org.dromara.system.domain.vo.SysSocialVoToSysSocialMapper
org.dromara.system.domain.vo.SysDictDataVoToSysDictDataMapper
org.dromara.hq.domain.HqSatDetailToHqSatDetailVoMapper
org.dromara.xg.domain.vo.XgCollectionBatchVoToXgCollectionBatchMapper
org.dromara.hq.domain.HqSatSurveyQuestionToHqSatSurveyQuestionVoMapper
org.dromara.system.domain.SysOssConfigToSysOssConfigVoMapper
org.dromara.system.domain.vo.SysLogininforVoToSysLogininforMapper
org.dromara.system.domain.bo.SysConfigBoToSysConfigMapper
org.dromara.hq.domain.bo.HqSatSurveyBoToHqSatSurveyMapper
org.dromara.system.domain.SysTenantPackageToSysTenantPackageVoMapper
org.dromara.system.domain.bo.SysPostBoToSysPostMapper
org.dromara.system.domain.vo.SysOssConfigVoToSysOssConfigMapper
org.dromara.system.domain.SysDictDataToSysDictDataVoMapper
org.dromara.hq.domain.bo.HqSatRecordBoToHqSatRecordMapper
org.dromara.system.domain.vo.SysRoleVoToSysRoleMapper
org.dromara.hq.domain.bo.HqHallReservationBoToHqHallReservationMapper
org.dromara.system.domain.vo.SysClientVoToSysClientMapper
org.dromara.system.domain.bo.SysOssConfigBoToSysOssConfigMapper
org.dromara.system.domain.bo.SysDeptBoToSysDeptMapper
org.dromara.hq.domain.vo.PmRepairAttachmentVoToPmRepairAttachmentMapper
org.dromara.hq.domain.HqSatCategoryToHqSatCategoryVoMapper
org.dromara.hq.domain.bo.HqSatDetailBoToHqSatDetailMapper
org.dromara.system.domain.bo.SysOssBoToSysOssMapper
org.dromara.system.domain.vo.SysMenuVoToSysMenuMapper
org.dromara.hq.domain.vo.HqHallInfoVoToHqHallInfoMapper
org.dromara.hq.domain.bo.HqHallInfoBoToHqHallInfoMapper
org.dromara.xg.domain.XgAuditRecordToXgAuditRecordVoMapper
org.dromara.xg.domain.XgStudentInfoToXgStudentInfoVoMapper
org.dromara.hq.domain.bo.HqVenueReservationBoToHqVenueReservationMapper
org.dromara.hq.domain.vo.PmRepairOrderVoToPmRepairOrderMapper
org.dromara.system.domain.bo.SysClientBoToSysClientMapper
org.dromara.system.domain.SysClientToSysClientVoMapper
org.dromara.hq.domain.HqVenueReservationToHqVenueReservationVoMapper
org.dromara.hq.domain.vo.HqSatCategoryVoToHqSatCategoryMapper
org.dromara.test.domain.XgStuMsgToXgStuMsgVoMapper
org.dromara.hq.domain.vo.HqSatRecordVoToHqSatRecordMapper
org.dromara.system.domain.SysTenantToSysTenantVoMapper
org.dromara.hq.domain.vo.HqHallReservationVoToHqHallReservationMapper
org.dromara.system.domain.vo.SysConfigVoToSysConfigMapper
org.dromara.xg.domain.vo.XgClassInfoVoToXgClassInfoMapper
org.dromara.system.domain.bo.SysSocialBoToSysSocialMapper
org.dromara.xg.domain.vo.XgStudentInfoVoToXgStudentInfoMapper
org.dromara.system.domain.vo.SysOssVoToSysOssMapper
org.dromara.system.domain.SysDictTypeToSysDictTypeVoMapper
org.dromara.hq.domain.HqHallReservationToHqHallReservationVoMapper
org.dromara.hq.domain.vo.HqVenueInfoVoToHqVenueInfoMapper
org.dromara.hq.domain.vo.HqSatOptionVoToHqSatOptionMapper
org.dromara.xg.domain.XgClassStudentToXgClassStudentVoMapper
org.dromara.hq.domain.bo.HqVenueInfoBoToHqVenueInfoMapper
org.dromara.hq.domain.bo.HqSatOptionBoToHqSatOptionMapper
org.dromara.hq.domain.bo.HqSatCategoryBoToHqSatCategoryMapper
org.dromara.system.domain.SysLogininforToSysLogininforVoMapper
org.dromara.web.domain.vo.TenantListVoToSysTenantVoMapper
org.dromara.system.domain.vo.SysPostVoToSysPostMapper
org.dromara.system.domain.SysSocialToSysSocialVoMapper
org.dromara.system.domain.bo.SysDictTypeBoToSysDictTypeMapper
org.dromara.common.log.event.OperLogEventToSysOperLogBoMapper
org.dromara.xg.domain.bo.XgStudentInfoBoToXgStudentInfoMapper
org.dromara.system.domain.bo.SysNoticeBoToSysNoticeMapper
org.dromara.system.domain.vo.SysSocialVoToSysSocialMapper
org.dromara.hq.domain.bo.HqSatSurveyQuestionBoToHqSatSurveyQuestionMapper
org.dromara.system.domain.SysOssConfigToSysOssConfigVoMapper
org.dromara.hq.domain.vo.HqSatSurveyQuestionVoToHqSatSurveyQuestionMapper
org.dromara.system.domain.SysUserToSysUserVoMapper
org.dromara.system.domain.vo.SysDictTypeVoToSysDictTypeMapper
org.dromara.test.domain.vo.XgStuMsgVoToXgStuMsgMapper
org.dromara.system.domain.SysDictDataToSysDictDataVoMapper
org.dromara.system.domain.bo.SysMenuBoToSysMenuMapper
org.dromara.hq.domain.HqVenueInfoToHqVenueInfoVoMapper
org.dromara.system.domain.vo.SysTenantVoToTenantListVoMapper
org.dromara.hq.domain.HqHallInfoToHqHallInfoVoMapper
org.dromara.system.domain.vo.SysRoleVoToSysRoleMapper
org.dromara.system.domain.vo.SysClientVoToSysClientMapper
org.dromara.system.domain.bo.SysOssConfigBoToSysOssConfigMapper
org.dromara.system.domain.SysConfigToSysConfigVoMapper
org.dromara.system.domain.bo.SysOperLogBoToOperLogEventMapper
org.dromara.hq.domain.PmRepairAttachmentToPmRepairAttachmentVoMapper
org.dromara.hq.domain.bo.PmRepairAttachmentBoToPmRepairAttachmentMapper
org.dromara.hq.domain.HqSatSurveyToHqSatSurveyVoMapper
org.dromara.hq.domain.vo.HqSatDetailVoToHqSatDetailMapper
org.dromara.system.domain.bo.SysDeptBoToSysDeptMapper
org.dromara.system.domain.vo.SysNoticeVoToSysNoticeMapper
org.dromara.hq.domain.bo.PmRepairOrderBoToPmRepairOrderMapper
org.dromara.hq.domain.HqSatQuestionToHqSatQuestionVoMapper
org.dromara.xg.domain.bo.XgClassInfoBoToXgClassInfoMapper
org.dromara.hq.domain.vo.HqVenueReservationVoToHqVenueReservationMapper
org.dromara.system.domain.bo.SysOssBoToSysOssMapper
org.dromara.xg.domain.vo.XgClassStudentVoToXgClassStudentMapper
org.dromara.system.domain.vo.SysMenuVoToSysMenuMapper
org.dromara.system.domain.SysMenuToSysMenuVoMapper
org.dromara.system.domain.vo.SysTenantVoToSysTenantMapper
org.dromara.system.domain.bo.SysUserBoToSysUserMapper
org.dromara.xg.domain.XgAuditRecordToXgAuditRecordVoMapper
org.dromara.xg.domain.XgStudentInfoToXgStudentInfoVoMapper
org.dromara.xg.domain.XgClassInfoToXgClassInfoVoMapper
org.dromara.system.domain.bo.SysClientBoToSysClientMapper
org.dromara.system.domain.bo.SysDictDataBoToSysDictDataMapper
org.dromara.hq.domain.vo.HqSatSurveyVoToHqSatSurveyMapper
org.dromara.system.domain.SysClientToSysClientVoMapper
org.dromara.system.domain.vo.SysUserVoToSysUserMapper
org.dromara.system.domain.SysNoticeToSysNoticeVoMapper
org.dromara.system.domain.SysOssToSysOssVoMapper
org.dromara.system.domain.SysOperLogToSysOperLogVoMapper
org.dromara.system.domain.bo.SysLogininforBoToSysLogininforMapper
org.dromara.system.domain.SysTenantToSysTenantVoMapper
org.dromara.system.domain.vo.SysDeptVoToSysDeptMapper
org.dromara.hq.domain.PmRepairCategoryToPmRepairCategoryVoMapper
org.dromara.hq.domain.bo.PmRepairCategoryBoToPmRepairCategoryMapper
org.dromara.hq.domain.PmRepairOrderToPmRepairOrderVoMapper
org.dromara.hq.domain.HqSatOptionToHqSatOptionVoMapper
org.dromara.hq.domain.vo.PmRepairCategoryVoToPmRepairCategoryMapper
org.dromara.system.domain.vo.SysConfigVoToSysConfigMapper
org.dromara.xg.domain.vo.XgClassInfoVoToXgClassInfoMapper
org.dromara.system.domain.bo.SysSocialBoToSysSocialMapper
org.dromara.system.domain.bo.SysRoleBoToSysRoleMapper
org.dromara.hq.domain.bo.HqSatQuestionBoToHqSatQuestionMapper
org.dromara.hq.domain.HqSatRecordToHqSatRecordVoMapper
org.dromara.xg.domain.vo.XgStudentInfoVoToXgStudentInfoMapper
org.dromara.system.domain.vo.SysOssVoToSysOssMapper
org.dromara.system.domain.SysDictTypeToSysDictTypeVoMapper
org.dromara.system.domain.SysRoleToSysRoleVoMapper
org.dromara.hq.domain.vo.HqSatQuestionVoToHqSatQuestionMapper
org.dromara.system.domain.vo.SysTenantPackageVoToSysTenantPackageMapper
org.dromara.xg.domain.XgClassStudentToXgClassStudentVoMapper
org.dromara.system.domain.bo.SysTenantBoToSysTenantMapper
org.dromara.system.domain.bo.SysTenantPackageBoToSysTenantPackageMapper
org.dromara.test.domain.bo.XgStuMsgBoToXgStuMsgMapper
org.dromara.system.domain.SysLogininforToSysLogininforVoMapper
org.dromara.system.domain.vo.SysPostVoToSysPostMapper
org.dromara.system.domain.SysSocialToSysSocialVoMapper
org.dromara.system.domain.bo.SysDictTypeBoToSysDictTypeMapper
org.dromara.common.log.event.OperLogEventToSysOperLogBoMapper
org.dromara.xg.domain.bo.XgStudentInfoBoToXgStudentInfoMapper
org.dromara.system.domain.bo.SysOperLogBoToSysOperLogMapper
org.dromara.xg.domain.vo.XgAuditRecordVoToXgAuditRecordMapper
org.dromara.system.domain.vo.SysOperLogVoToSysOperLogMapper
org.dromara.system.domain.SysDeptToSysDeptVoMapper
org.dromara.system.domain.bo.SysNoticeBoToSysNoticeMapper
org.dromara.xg.domain.XgCollectionBatchToXgCollectionBatchVoMapper
org.dromara.system.domain.SysPostToSysPostVoMapper
-1
View File
@@ -1 +0,0 @@
Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

-4
View File
@@ -1,4 +0,0 @@
测试附件内容
创建时间: 6/30/2025, 7:16:26 PM
文件用途: 验证附件上传功能
测试编号: ATT-1751282186462
-4
View File
@@ -1,4 +0,0 @@
测试附件内容
创建时间: 6/30/2025, 7:17:10 PM
文件用途: 验证附件上传功能
测试编号: ATT-1751282230141
-4
View File
@@ -1,4 +0,0 @@
测试附件内容
创建时间: 6/30/2025, 7:17:38 PM
文件用途: 验证附件上传功能
测试编号: ATT-1751282258774
+23 -68
View File
@@ -1,84 +1,39 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { HallReservationVO, HallReservationForm, HallReservationQuery } from '@/api/hq/hallReservation/types';
import { HallReservationVO, HallReservationQuery } from '../hallReservation/types';
import { HallReservationAuditForm, TimeCheckParams, TimeCheckResult } from './types';
import { TableDataInfo, R } from '@/api/types';
/**
* 查询报告厅预约列表
* @param query
* @returns {*}
*/
export const listHallReservation = (query?: HallReservationQuery): AxiosPromise<HallReservationVO[]> => {
return request({
// 查询报告厅预约列表
export function listHallReservation(query: HallReservationQuery) {
return request<TableDataInfo<HallReservationVO>>({
url: '/hq/hallReservation/list',
method: 'get',
params: query
});
};
}
/**
* 查询报告厅预约详细
* @param id
*/
export const getHallReservation = (id: string | number): AxiosPromise<HallReservationVO> => {
return request({
// 获取报告厅预约详细信息
export function getHallReservation(id: string | number) {
return request<R<HallReservationVO>>({
url: '/hq/hallReservation/' + id,
method: 'get'
});
};
/**
* 新增报告厅预约
* @param data
*/
export const addHallReservation = (data: HallReservationForm) => {
return request({
url: '/hq/hallReservation',
method: 'post',
data: data
});
};
/**
* 修改报告厅预约
* @param data
*/
export const updateHallReservation = (data: HallReservationForm) => {
return request({
url: '/hq/hallReservation',
method: 'put',
data: data
});
};
/**
* 删除报告厅预约
* @param id
*/
export const delHallReservation = (id: string | number | Array<string | number>) => {
return request({
url: '/hq/hallReservation/' + id,
method: 'delete'
});
};
/**
* 审核报告厅预约(批准/拒绝)
* @param data
*/
export interface HallReservationAuditForm {
/** 预约记录ID列表 */
ids: Array<string | number>;
/** 审核状态:1-批准,2-拒绝 */
status: string;
/** 审核备注 */
auditRemark?: string;
}
export const auditHallReservation = (data: HallReservationAuditForm) => {
return request({
// 审核报告厅预约
export function auditHallReservation(data: HallReservationAuditForm) {
return request<R<any>>({
url: '/hq/hallReservation/audit',
method: 'put',
data: data
});
};
}
// 检查报告厅预约时间是否存在冲突
export function checkTimeConflict(data: TimeCheckParams) {
return request<R<TimeCheckResult>>({
url: '/hq/hallReservation/checkTimeConflict',
method: 'post',
data: data
});
}
+26 -187
View File
@@ -1,200 +1,39 @@
export interface HallReservationVO {
/**
* 主键ID
*/
id: string | number;
import { HallReservationVO } from '../hallReservation/types';
/**
* 报告厅ID
*/
hallId: string | number;
/**
* 报告厅名称
*/
hallName: string;
/**
* 开始时间
*/
startTime: string;
/**
* 结束时间
*/
endTime: string;
/**
* 预约用途
*/
reservationPurpose: string;
/**
* 申请人id
*/
applicantId: string | number;
/**
* 申请人姓名
*/
applicantName: string;
/**
* 申请人电话
*/
applicantPhone: string;
/**
* 预约状态
* 报告厅预约审核表单
*/
export interface HallReservationAuditForm {
/** 预约ID列表 */
ids: Array<string | number>;
/** 审核状态 */
status: string;
/**
* 审核人id
*/
auditBy: number;
/**
* 审核人
*/
auditName: string;
/**
* 审核时间
*/
auditTime: string;
/**
* 审核备注
*/
auditRemark: string;
}
export interface HallReservationForm extends BaseEntity {
/**
* 主键ID
*/
id?: string | number;
/**
* 报告厅ID
*/
hallId?: string | number;
/**
* 报告厅名称
*/
hallName?: string;
/**
* 开始时间
*/
startTime?: string;
/**
* 结束时间
*/
endTime?: string;
/**
* 预约用途
*/
reservationPurpose?: string;
/**
* 申请人id
*/
applicantId?: string | number;
/**
* 申请人姓名
*/
applicantName?: string;
/**
* 申请人电话
*/
applicantPhone?: string;
/**
* 预约状态
*/
status?: string;
/**
* 审核人id
*/
auditBy?: number;
/**
* 审核人
*/
auditName?: string;
/**
* 审核时间
*/
auditTime?: string;
/**
* 审核备注
*/
/** 审核备注 */
auditRemark?: string;
}
export interface HallReservationQuery extends PageQuery {
/**
* 时间冲突检查参数
*/
export interface TimeCheckParams {
/** 报告厅ID */
hallId: string | number;
/** 预约开始时间 */
startTime: string;
/** 预约结束时间 */
endTime: string;
/** 排除的预约ID */
excludeId?: string | number;
}
/**
* 报告厅ID
* 时间冲突检查结果
*/
hallId?: string | number;
/**
* 报告厅名称
*/
hallName?: string;
/**
* 开始时间
*/
startTime?: string;
/**
* 结束时间
*/
endTime?: string;
/**
* 开始时间查询参数(字符串格式)
*/
startTimeStr?: string;
/**
* 结束时间查询参数(字符串格式)
*/
endTimeStr?: string;
/**
* 申请人ID
*/
applicantId?: string | number;
/**
* 申请人姓名
*/
applicantName?: string;
/**
* 预约状态
*/
status?: string;
/**
* 日期范围参数
*/
params?: any;
export interface TimeCheckResult {
/** 是否存在冲突 */
hasConflict: boolean;
/** 冲突的预约列表 */
conflictList: HallReservationVO[];
}
+1 -1
View File
@@ -1,6 +1,6 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { RepairCategoryVO, RepairCategoryForm, RepairCategoryQuery } from '@/api/hq/hq/pm/types';
import { RepairCategoryVO, RepairCategoryForm, RepairCategoryQuery } from '@/api/hq/pm/types';
/**
* 查询维修类型列表
+15 -1
View File
@@ -209,6 +209,19 @@ export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
});
};
/** 获取具有指定权限的用户列表 */
export function listUsersByPermission(permission: string, tenantId?: string): AxiosPromise<UserVO[]> {
return request({
url: '/system/user/list-by-permission',
method: 'get',
params: {
permission,
tenantId
}
});
}
export default {
listUser,
getUser,
@@ -225,5 +238,6 @@ export default {
getAuthRole,
updateAuthRole,
deptTreeSelect,
listUserByDeptId
listUserByDeptId,
listUsersByPermission
};
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752052172719" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8134" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M756.01248 336.814072l-20.336164 0 0 510.696308-67.101257 0L668.575058 336.814072 556.73383 336.814072l0 510.696308-67.106374 0L489.627457 336.814072 377.791345 336.814072l0 510.696308-67.102281 0L310.689065 336.814072l-42.702568 0c-50.532907 0-91.50711-0.727571-91.50711 0l0 533.587668c0 49.123814 40.974203 88.944751 91.50711 88.944751l488.019843 0c50.544163 0 91.50097-39.820937 91.50097-88.944751L847.50731 336.814072C847.512427 333.065701 806.562782 336.814072 756.01248 336.814072L756.01248 336.814072zM891.467521 176.488597 690.943508 176.488597l0-54.57292c0-31.630395-56.529481-57.263192-89.479941-57.263192L422.525176 64.652486c-32.933063 0-89.468684 25.632797-89.468684 57.263192l0 54.57292L132.566248 176.488597c-25.160029 0-45.548382 20.033265-45.548382 44.740994 0 24.695448 20.382213 44.727691 45.548382 44.727691L891.467521 265.957281c25.137517 0 45.514613-20.032242 45.514613-44.727691C936.975994 196.521862 916.593781 176.488597 891.467521 176.488597L891.467521 176.488597zM601.468684 176.488597 422.530292 176.488597l0-22.367427 178.938392 0L601.468684 176.488597 601.468684 176.488597zM601.468684 176.488597" fill="#272636" p-id="8135"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752052134276" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5447" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M220.094304 322.742967l247.380709-82.457849c33.513211-11.171411 70.457561-2.450813 95.438462 22.529064l198.273366 198.270297c24.979878 24.977831 33.704569 61.924228 22.534181 95.439485l-82.463989 247.376616a93.251661 93.251661 0 0 1-64.445649 60.616444L63.720126 1017.340337c-24.88164 6.634081-50.436615-8.158804-57.070696-33.040445a46.613551 46.613551 0 0 1 0-24.028205L159.471719 387.186569a93.250638 93.250638 0 0 1 60.622585-64.443602m125.403877 420.158452a91.065883 91.065883 0 0 1 70.947724-26.441156c1.682312 0.12075 3.266387 0.181125 4.754271 0.181125 62.869761 0 113.84054-50.966687 113.84054-113.842587 0-62.87283-50.97078-113.841564-113.84054-113.841564-62.87283 0-113.84361 50.968733-113.84361 113.841564 0 1.490954 0.061398 3.072982 0.182148 4.752224a91.080209 91.080209 0 0 1-26.437063 70.94977L156.663773 802.936627c-17.781956 17.781956-17.781956 46.617644 0 64.398577 17.781956 17.784003 46.615597 17.784003 64.401647 0l124.432761-124.433785zM579.671104 80.436823c100.483351-100.484375 263.404629-100.484375 363.89105 0 100.484375 100.485398 100.484375 263.404629 0 363.890027-28.784523 28.784523-75.449239 28.784523-104.229668 0L579.671104 184.666491c-28.783499-28.781453-28.783499-75.448215 0-104.229668" fill="#040000" p-id="5448"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752052118757" class="icon" viewBox="0 0 1829 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4426" xmlns:xlink="http://www.w3.org/1999/xlink" width="357.2265625" height="200"><path d="M914.297945 0C314.456393 0 0 441.928431 0 512.019026c0 70.052544 314.456393 511.980974 914.297945 511.980974 599.8035 0 914.297945-441.928431 914.297945-511.980974C1828.633942 441.928431 1514.101446 0 914.297945 0zM914.297945 905.888596c-224.46494 0-406.388466-176.329977-406.388466-393.86957 0-217.539593 181.923526-393.86957 406.388466-393.86957 224.426889 0 406.388466 176.291925 406.388466 393.86957C1320.686411 729.558619 1138.762885 905.888596 914.297945 905.888596zM914.297945 512.019026c-37.252276-40.867155 60.653859-196.953811 0-196.953811-112.213444 0-203.194233 88.20304-203.194233 196.953811s90.980789 196.915759 203.194233 196.915759c112.175393 0 203.194233-88.164988 203.194233-196.915759C1117.492178 462.057597 945.918621 546.759912 914.297945 512.019026z" fill="#090909" p-id="4427"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1686,3 +1686,8 @@ body {
outline: none !important;
}
}
// 隐藏图片上传列表的删除按钮
.el-upload-list--picture-card .el-upload-list__item .el-icon--close {
display: none !important;
}
+7 -6
View File
@@ -441,12 +441,13 @@ img {
}
// 禁用状态
.disabled,
[disabled] {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none;
}
// 禁用状态取消,让分页组件可以点击跳转
// .disabled,
// [disabled] {
// opacity: 0.6;
// cursor: not-allowed;
// pointer-events: none;
// }
// 加载状态
.loading {
+2 -1
View File
@@ -4,7 +4,8 @@
:root {
// Theme color
// --art-primary: 50, 50, 222;
--art-primary: 93, 135, 255;
// --art-primary: 93, 135, 255;
--art-primary: 0, 58, 255;
--art-secondary: 73, 190, 255;
--art-error: 250, 137, 107;
--art-info: 0, 75, 178;
@@ -9,6 +9,7 @@
:to="{ path: tag.path ? tag.path : '', query: tag.query }"
class="tags-view-item"
:style="activeStyle(tag)"
@click="handleTagClick(tag, $event)"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)"
>
@@ -232,6 +233,27 @@ const handleScroll = () => {
closeMenu();
};
// 点击标签时,如果标签已激活,则执行刷新操作
const handleTagClick = (tag: RouteLocationNormalized, e: MouseEvent) => {
// 阻止 router-link 默认行为,统一由脚本控制导航和刷新
e.preventDefault();
if (isActive(tag)) {
// 当前已激活,直接刷新
refreshSelectedTag(tag);
} else {
// 尚未激活,先导航到对应路由,导航成功后再刷新
router
.push({ path: tag.path || '', query: tag.query })
.then(() => {
refreshSelectedTag(tag);
})
.catch(() => {
/* 忽略重复导航错误 */
});
}
};
onMounted(() => {
initTags();
addTags();
+540 -40
View File
@@ -3,31 +3,65 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div ref="searchRef" v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="报告厅ID" prop="hallId">
<el-input v-model="queryParams.hallId" placeholder="请输入报告厅ID" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 第一行:4个检索元素 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="报告厅名称" prop="hallName">
<el-input v-model="queryParams.hallName" placeholder="请输入报告厅名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="预约开始时间" prop="startTime">
<el-date-picker clearable v-model="queryParams.startTime" type="date" placeholder="请选择开始时间" value-format="YYYY-MM-DD" />
<el-date-picker
clearable
v-model="queryParams.startTime"
type="date"
placeholder="请选择开始时间"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="预约结束时间" prop="endTime">
<el-date-picker clearable v-model="queryParams.endTime" type="date" placeholder="请选择结束时间" value-format="YYYY-MM-DD" />
<el-date-picker
clearable
v-model="queryParams.endTime"
type="date"
placeholder="请选择结束时间"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请人姓名" prop="applicantName">
<el-input v-model="queryParams.applicantName" placeholder="请输入申请人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<!-- 第二行:剩余检索元素 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="预约状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择预约状态" clearable>
<el-select v-model="queryParams.status" placeholder="请选择预约状态" clearable style="width: 100%">
<el-option v-for="dict in hall_reserv_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 操作按钮行 -->
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
@@ -53,7 +87,14 @@
</el-row>
</template>
<el-table v-loading="loading" border :data="hallReservationList" @selection-change="handleSelectionChange" style="margin-bottom: 0">
<el-table
v-loading="loading"
border
:data="hallReservationList"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
style="margin-bottom: 0"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="报告厅ID" align="center" prop="hallId" />
<el-table-column label="报告厅名称" align="center" prop="hallName" />
@@ -80,13 +121,9 @@
:hide-after="100"
effect="dark"
>
<span class="purpose-text">
{{
scope.row.reservationPurpose && scope.row.reservationPurpose.length > 100
? scope.row.reservationPurpose.substring(0, 100) + '...'
: scope.row.reservationPurpose
}}
</span>
<div class="purpose-text line-clamp-3">
{{ scope.row.reservationPurpose }}
</div>
</el-tooltip>
</template>
</el-table-column>
@@ -112,17 +149,13 @@
:hide-after="100"
effect="dark"
>
<span class="audit-remark-text">
{{
scope.row.auditRemark && scope.row.auditRemark.length > 100
? scope.row.auditRemark.substring(0, 100) + '...'
: scope.row.auditRemark
}}
</span>
<div class="audit-remark-text line-clamp-3">
{{ scope.row.auditRemark }}
</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220" fixed="right">
<template #default="scope">
<!-- 查看按钮,所有记录都可以查看 -->
<el-tooltip content="查看" placement="top">
@@ -180,9 +213,9 @@
<span class="form-text">{{ viewForm.hallName }}</span>
</el-form-item>
<!-- 报告厅图片展示区域 -->
<el-form-item label="报告厅图片" v-if="viewHallImages.length > 0">
<el-form-item label="报告厅图片">
<div class="view-hall-images">
<div class="image-gallery">
<div class="image-gallery" v-if="viewHallImages.length > 0">
<div v-for="(image, index) in viewHallImages" :key="index" class="image-item" @click="previewImage(image, index)">
<img :src="image" class="gallery-image" @error="handleImageError" />
<div class="image-overlay">
@@ -190,6 +223,10 @@
</div>
</div>
</div>
<div v-else class="no-images-placeholder">
<el-icon><Picture /></el-icon>
<span>暂无图片</span>
</div>
</div>
</el-form-item>
<el-form-item label="预约开始时间">
@@ -265,8 +302,7 @@
<div class="reservation-details">
<div class="detail-row">
<span class="detail-label">申请人:</span>
<span class="detail-value">{{ record.applicantName }}</span>
<span class="detail-phone">({{ record.applicantPhone || '手机号暂无' }})</span>
<span class="detail-value"> {{ record.applicantName }}({{ record.applicantPhone || '手机号暂无' }}) </span>
</div>
<div class="detail-row">
<span class="detail-label">预约时间:</span>
@@ -278,6 +314,46 @@
<span class="detail-label">预约用途:</span>
<span class="detail-value purpose-content">{{ record.reservationPurpose }}</span>
</div>
<div class="detail-row">
<span class="detail-label">审核建议:</span>
<span class="detail-value audit-suggestion-content" :class="getConflictStatusClass(record)">
<template v-if="getConflictCheckStatus(record).isLoading">
<el-icon class="rotate-loading"><Loading /></el-icon> 正在检查时间冲突...
</template>
<template v-else-if="getConflictCheckStatus(record).hasError">
<el-icon class="text-warning"><Warning /></el-icon> 冲突检查失败,请手动核实
</template>
<template v-else-if="hasTimeConflict(record)">
<el-icon class="text-danger"><Warning /></el-icon> 发现时间冲突,建议拒绝
<div class="conflict-details" v-if="getConflictReservations(record).length > 0">
<small>冲突详情:</small>
<ul class="conflict-list">
<li v-for="conflict in getConflictReservations(record)" :key="conflict.id">
<el-tag type="warning" size="small" v-if="conflict.internal">本次批次</el-tag>
<el-tag type="info" size="small" v-else>已审批</el-tag>
{{ conflict.applicantName }} ({{ parseTime(conflict.startTime, '{h}:{i}') }}-{{
parseTime(conflict.endTime, '{h}:{i}')
}})
</li>
</ul>
</div>
</template>
<template v-else-if="getConflictCheckStatus(record).isChecked">
<el-icon class="text-success"><Check /></el-icon> 无时间冲突,可以批准
</template>
<template v-else>
<el-icon class="text-info"><InfoFilled /></el-icon> 未检查时间冲突
<el-button
type="text"
size="small"
@click="manualCheckConflict(record)"
:loading="getConflictCheckStatus(record).isLoading"
>
立即检查
</el-button>
</template>
</span>
</div>
</div>
</div>
</div>
@@ -294,6 +370,22 @@
{{ auditForm.status === '1' ? '批准' : '拒绝' }}
</el-tag>
</div>
<!-- 批准时显示冲突检查统计 -->
<div v-if="auditForm.status === '1'" class="conflict-summary">
<span class="audit-label">冲突检查统计:</span>
<div class="summary-stats">
<el-tag type="info" size="small"> 总计:{{ selectedRecords.length }} 个预约 </el-tag>
<el-tag type="success" size="small"> 无冲突:{{ getNoConflictCount() }} 个 </el-tag>
<el-tag type="danger" size="small" v-if="getConflictCount() > 0"> 有冲突:{{ getConflictCount() }} 个 </el-tag>
<el-tag type="warning" size="small" v-if="getUncheckCount() > 0"> 未检查:{{ getUncheckCount() }} 个 </el-tag>
</div>
<div v-if="getConflictCount() > 0" class="conflict-warning-text">
<el-icon><Warning /></el-icon>
建议先处理时间冲突的预约,或选择拒绝有冲突的申请
</div>
</div>
<div class="audit-remark-section">
<span class="audit-label">审核备注:</span>
<el-input
@@ -320,7 +412,8 @@
</template>
<script setup name="HallAudit" lang="ts">
import { listHallReservation, getHallReservation, auditHallReservation, HallReservationAuditForm } from '@/api/hq/hallAudit';
import { listHallReservation, getHallReservation, auditHallReservation, HallReservationAuditForm, checkTimeConflict } from '@/api/hq/hallAudit';
import dayjs from 'dayjs';
import { listHallInfo } from '@/api/hq/hallInfo';
import { HallReservationVO, HallReservationQuery, HallReservationForm } from '@/api/hq/hallReservation/types';
import { HallInfoVO } from '@/api/hq/hallInfo/types';
@@ -385,6 +478,10 @@ const auditForm = ref({
const selectedRecords = ref<HallReservationVO[]>([]);
const auditLoading = ref(false);
// 审核建议相关状态
const conflictCheckResults = reactive(new Map<string | number, any>());
const conflictCheckLoading = reactive(new Set<string | number>());
const queryParams = ref<HallReservationQuery>({
pageNum: 1,
pageSize: 10,
@@ -430,7 +527,7 @@ const getHallList = async () => {
const getHallImages = (imageUrl: string): string[] => {
if (!imageUrl || imageUrl.trim() === '') return [];
// 支持多种分隔符:逗号、分号、空格
const separators = /[,;|\s]+/;
const separators = /[,;|\\s]+/;
const images = imageUrl
.split(separators)
.map((url) => url.trim())
@@ -680,13 +777,22 @@ const handleReject = async (row?: HallReservationVO) => {
};
/** 打开审核对话框 */
const openAuditDialog = (status: string, ids: Array<string | number>, records: HallReservationVO[], title: string) => {
const openAuditDialog = async (status: string, ids: Array<string | number>, records: HallReservationVO[], title: string) => {
auditForm.value.status = status;
auditForm.value.auditRemark = '';
auditForm.value.ids = ids;
selectedRecords.value = records;
auditDialog.title = title;
auditDialog.visible = true;
// 在打开审核对话框时统一检查时间冲突(无论批准/拒绝)
// 清除选中记录旧的缓存(尤其是 internal 冲突信息)
records.forEach((rec) => conflictCheckResults.delete(rec.id));
if (records.length > 0) {
console.log('审核对话框打开,开始检查时间冲突...');
await batchCheckTimeConflict(records);
}
};
/** 确认审核操作 */
@@ -753,6 +859,242 @@ const handleExport = () => {
);
};
/** 检查单个记录的时间冲突 */
const checkRecordTimeConflict = async (record: HallReservationVO) => {
const recordId = record.id;
// 如果已经在检查中,跳过
if (conflictCheckLoading.has(recordId)) {
return;
}
// 如果已有缓存结果,直接返回
if (conflictCheckResults.has(recordId)) {
return conflictCheckResults.get(recordId);
}
try {
conflictCheckLoading.add(recordId);
const res = await checkTimeConflict({
hallId: record.hallId,
startTime: dayjs(record.startTime).format('YYYY-MM-DD HH:mm:ss'),
endTime: dayjs(record.endTime).format('YYYY-MM-DD HH:mm:ss'),
excludeId: record.id
});
const result = {
hasConflict: res.data.hasConflict,
conflictList: (res.data.conflictList || []).map((item: any) => ({ ...item, internal: false })),
conflictMessage: res.data.conflictMessage || '',
checkTime: new Date().getTime()
};
// 缓存结果
conflictCheckResults.set(recordId, result);
return result;
} catch (error) {
console.error('检查时间冲突失败:', error);
const errorResult = {
hasConflict: false,
conflictList: [],
conflictMessage: '冲突检查失败',
checkTime: new Date().getTime(),
error: true
};
conflictCheckResults.set(recordId, errorResult);
return errorResult;
} finally {
conflictCheckLoading.delete(recordId);
}
};
/** 批量检查选中记录的时间冲突 */
const batchCheckTimeConflict = async (records: HallReservationVO[]) => {
console.log('开始批量检查时间冲突,记录数:', records.length);
const promises = records.map((record) => checkRecordTimeConflict(record));
await Promise.all(promises);
// 前端额外校验勾选记录之间的时间交叉冲突(同一报告厅)
addInternalConflictInfo(records);
console.log('批量时间冲突检查完成');
};
/** 判断两个时间段是否重叠 */
const isTimeOverlap = (startA: any, endA: any, startB: any, endB: any) => {
const sA = dayjs(startA);
const eA = dayjs(endA);
const sB = dayjs(startB);
const eB = dayjs(endB);
return sA.isBefore(eB) && sB.isBefore(eA); // 开始 < 另一结束 且 另一开始 < 结束
};
/** 在选中记录之间标记内部冲突 */
const addInternalConflictInfo = (records: HallReservationVO[]) => {
// 若不足两条记录无需内部冲突检测
if (records.length < 2) return;
const hallGroups: Record<string, HallReservationVO[]> = {};
records.forEach((rec) => {
const key = `${rec.hallId || ''}`;
hallGroups[key] = hallGroups[key] || [];
hallGroups[key].push(rec);
});
Object.values(hallGroups).forEach((list) => {
// 对同一报告厅的申请做两两比较
for (let i = 0; i < list.length; i++) {
for (let j = i + 1; j < list.length; j++) {
const r1 = list[i];
const r2 = list[j];
if (isTimeOverlap(r1.startTime, r1.endTime, r2.startTime, r2.endTime)) {
// r1 冲突信息
const res1 = conflictCheckResults.get(r1.id) || {
hasConflict: false,
conflictList: [],
conflictMessage: '',
checkTime: new Date().getTime()
};
res1.hasConflict = true;
res1.conflictList.push({ ...r2, internal: true });
conflictCheckResults.set(r1.id, res1);
// r2 冲突信息
const res2 = conflictCheckResults.get(r2.id) || {
hasConflict: false,
conflictList: [],
conflictMessage: '',
checkTime: new Date().getTime()
};
res2.hasConflict = true;
res2.conflictList.push({ ...r1, internal: true });
conflictCheckResults.set(r2.id, res2);
}
}
}
});
};
/** 获取记录的冲突检查结果 */
const getConflictResult = (record: HallReservationVO) => {
return (
conflictCheckResults.get(record.id) || {
hasConflict: false,
conflictList: [],
conflictMessage: '未检查',
checkTime: 0
}
);
};
/** 检查是否存在时间冲突(同步方法) */
const hasTimeConflict = (record: HallReservationVO): boolean => {
const result = getConflictResult(record);
return result.hasConflict;
};
/** 获取时间冲突的预约列表(同步方法) */
const getConflictReservations = (record: HallReservationVO) => {
const result = getConflictResult(record);
return result.conflictList || [];
};
/** 获取冲突检查状态 */
const getConflictCheckStatus = (record: HallReservationVO) => {
const isLoading = conflictCheckLoading.has(record.id);
const result = getConflictResult(record);
return {
isLoading,
isChecked: result.checkTime > 0,
hasError: result.error || false,
result
};
};
/** 获取冲突状态对应的CSS类 */
const getConflictStatusClass = (record: HallReservationVO) => {
const status = getConflictCheckStatus(record);
if (status.isLoading) {
return 'checking-conflict';
}
if (status.hasError) {
return 'check-error';
}
if (status.isChecked) {
return hasTimeConflict(record) ? 'has-conflict' : 'no-conflict';
}
return 'not-checked';
};
/** 获取审核建议文本 */
const getAuditSuggestionText = (record: HallReservationVO) => {
const status = getConflictCheckStatus(record);
if (status.isLoading) {
return '正在检查时间冲突...';
}
if (status.hasError) {
return '冲突检查失败,请手动核实';
}
if (status.isChecked) {
return hasTimeConflict(record) ? '发现时间冲突,建议拒绝' : '无时间冲突,可以批准';
}
return '未检查时间冲突';
};
/** 手动触发时间冲突检查 */
const manualCheckConflict = async (record: HallReservationVO) => {
// 清除缓存,强制重新检查
conflictCheckResults.delete(record.id);
await checkRecordTimeConflict(record);
};
/** 获取无冲突的预约数量 */
const getNoConflictCount = () => {
return selectedRecords.value.filter((record) => {
const status = getConflictCheckStatus(record);
return status.isChecked && !hasTimeConflict(record);
}).length;
};
/** 获取有冲突的预约数量 */
const getConflictCount = () => {
return selectedRecords.value.filter((record) => {
const status = getConflictCheckStatus(record);
return status.isChecked && hasTimeConflict(record);
}).length;
};
/** 获取未检查的预约数量 */
const getUncheckCount = () => {
return selectedRecords.value.filter((record) => {
const status = getConflictCheckStatus(record);
return !status.isChecked && !status.isLoading;
}).length;
};
/** 获取预约状态文本 */
const getStatusText = (status: string) => {
const statusMap: { [key: string]: string } = {
'0': '审核中',
'1': '已批准',
'2': '已拒绝'
};
return statusMap[status] || status;
};
/** 页面加载完成后的初始化 */
onMounted(() => {
getList();
getHallList();
@@ -855,22 +1197,31 @@ onMounted(() => {
/* 表格中预约用途文本样式 */
.purpose-text {
display: inline-block;
max-width: 100%;
display: -webkit-box;
word-break: break-all;
word-wrap: break-word;
line-height: 1.4;
cursor: pointer;
padding: 0 4px;
}
/* 表格中审核备注文本样式 */
.audit-remark-text {
display: inline-block;
max-width: 100%;
display: -webkit-box;
word-break: break-all;
word-wrap: break-word;
line-height: 1.4;
cursor: pointer;
padding: 0 4px;
}
/* 三行省略样式 */
.line-clamp-3 {
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
max-height: 4.2em;
}
/* 查看详情表单样式 */
@@ -920,11 +1271,6 @@ onMounted(() => {
width: 100%;
}
/* 选中报告厅的图片展示区域 */
.selected-hall-images {
width: 100%;
}
.image-gallery {
display: flex;
flex-wrap: wrap;
@@ -977,6 +1323,30 @@ onMounted(() => {
font-size: 20px;
}
/* 暂无图片占位样式 */
.no-images-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
max-width: 450px;
height: 120px;
background-color: #f5f7fa;
border: 2px dashed #dcdfe6;
border-radius: 8px;
color: #909399;
}
.no-images-placeholder .el-icon {
font-size: 32px;
margin-bottom: 8px;
}
.no-images-placeholder span {
font-size: 14px;
}
/* 审核对话框样式 */
.audit-form .form-text {
color: #303133;
@@ -1026,7 +1396,7 @@ onMounted(() => {
.hall-id {
font-size: 14px;
color: #909399;
background: #f0f2f5;
/* background: #f0f2f5; */
padding: 4px 8px;
border-radius: 4px;
}
@@ -1161,6 +1531,136 @@ onMounted(() => {
font-size: 14px;
resize: none !important;
}
/* 审核建议样式 - 增强版 */
.audit-suggestion-content {
line-height: 1.6;
display: inline-flex;
align-items: center;
gap: 4px;
flex-wrap: wrap;
}
.audit-suggestion-content .el-icon {
margin-right: 4px;
}
/* 冲突详情在新行展示 */
.audit-suggestion-content .conflict-details {
flex-basis: 100%;
}
/* 不同状态的颜色 */
.audit-suggestion-content.checking-conflict {
color: #409eff; /* 检查中 - 蓝色 */
}
.audit-suggestion-content.check-error {
color: #e6a23c; /* 检查错误 - 橙色 */
}
.audit-suggestion-content.has-conflict {
color: #f56c6c; /* 有冲突 - 红色 */
}
.audit-suggestion-content.no-conflict {
color: #67c23a; /* 无冲突 - 绿色 */
}
.audit-suggestion-content.not-checked {
color: #909399; /* 未检查 - 灰色 */
}
/* 冲突详情样式 */
.conflict-details {
margin-top: 8px;
padding: 8px;
background-color: #fef2f2;
border-left: 3px solid #f56c6c;
border-radius: 4px;
font-size: 12px;
}
.conflict-list {
margin: 4px 0 0 0;
padding-left: 16px;
list-style-type: disc;
}
.conflict-list li {
margin: 2px 0;
color: #666;
}
/* 文本颜色工具类 */
.text-success {
color: #67c23a;
}
.text-warning {
color: #e6a23c;
}
.text-danger {
color: #f56c6c;
}
.text-info {
color: #409eff;
}
/* 加载动画 */
.rotate-loading {
animation: rotate 2s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 冲突检查统计样式 */
.conflict-summary {
margin: 0px 0;
padding: 0px;
/* background-color: #f9fafe; */
border-radius: 6px;
/* border: 1px solid #e4e7ed; */
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap; /* 超出时自动换行 */
}
.summary-stats {
display: inline-flex; /* 与标签同行 */
gap: 8px;
margin-top: 0; /* 移除顶部间距,保持同一行 */
flex-wrap: wrap; /* 达到最大宽度时自身也可换行 */
}
.conflict-warning-text {
margin-top: 8px;
color: #e6a23c;
font-size: 14px;
display: flex;
align-items: center;
gap: 4px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>
<style>
+88 -9
View File
@@ -3,19 +3,30 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="报告厅名称" prop="hallName">
<el-input v-model="queryParams.hallName" placeholder="请输入报告厅名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="报告厅状态" prop="hallStatus">
<el-select v-model="queryParams.hallStatus" placeholder="请选择报告厅状态" clearable>
<el-select v-model="queryParams.hallStatus" placeholder="请选择报告厅状态" clearable style="width: 100%">
<el-option v-for="dict in hall_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
@@ -40,11 +51,11 @@
</el-row>
</template>
<el-table v-loading="loading" border :data="hallInfoList" @selection-change="handleSelectionChange" style="margin-bottom: 20px">
<el-table v-loading="loading" border :data="hallInfoList" @selection-change="handleSelectionChange" @row-dblclick="handleView">
<el-table-column type="selection" width="55" align="center" />
<!-- <el-table-column label="主键ID" align="center" prop="id" v-if="true" /> -->
<el-table-column label="报告厅名称" align="center" prop="hallName" />
<el-table-column label="报告厅图片" align="center" prop="hallImage" width="100">
<el-table-column label="报告厅图片" align="center" prop="hallImage" width="200">
<template #default="scope">
<div v-if="scope.row.hallImage" class="table-image">
<image-preview :src="scope.row.hallImage" :width="50" :height="50" />
@@ -54,21 +65,25 @@
</div>
</template>
</el-table-column>
<el-table-column label="报告厅状态" align="center" prop="hallStatus">
<el-table-column label="报告厅状态" align="center" prop="hallStatus" width="120">
<template #default="scope">
<dict-tag :options="hall_status" :value="scope.row.hallStatus" />
</template>
</el-table-column>
<el-table-column label="报告厅描述" align="center" prop="hallDescription" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160">
<el-table-column label="报告厅描述" align="center" prop="hallDescription">
<template #default="scope">
<div class="description-cell">{{ scope.row.hallDescription }}</div>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="info"
type="primary"
icon="View"
size="default"
style="font-size: 16px; margin-right: 8px"
style="font-size: 16px; margin-right: 8px; color: blue"
@click="handleView(scope.row)"
v-hasPermi="['hq:hallInfo:query']"
></el-button>
@@ -477,4 +492,68 @@ onMounted(() => {
background-color: #fff;
margin-top: 0px !important;
}
.el-popper.description-tooltip-popper {
width: 300px !important;
max-width: 300px !important;
}
.el-popper.description-tooltip-popper .el-popper__content {
width: 300px !important;
max-width: 300px !important;
}
.el-popper.description-tooltip-popper .el-tooltip__content {
width: 100% !important;
max-height: 200px !important;
overflow-y: auto !important;
white-space: pre-wrap !important;
word-break: break-all !important;
text-align: left !important;
line-height: 1.5 !important;
padding: 8px 12px !important;
/* 自定义滚动条样式 */
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background-color: rgba(144, 147, 153, 0.3);
&:hover {
background-color: rgba(144, 147, 153, 0.5);
}
}
&::-webkit-scrollbar-track {
border-radius: 3px;
background-color: transparent;
}
}
.description-cell {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.5;
max-height: 4.5em;
text-align: left;
padding: 0 10px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>
+247 -46
View File
@@ -3,28 +3,54 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div ref="searchRef" v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="报告厅ID" prop="hallId">
<el-input v-model="queryParams.hallId" placeholder="请输入报告厅ID" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="报告厅名称" prop="hallName">
<el-input v-model="queryParams.hallName" placeholder="请输入报告厅名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="预约开始时间" prop="startTime">
<el-date-picker clearable v-model="queryParams.startTime" type="date" placeholder="请选择开始时间" value-format="YYYY-MM-DD" />
<el-date-picker
clearable
v-model="queryParams.startTime"
type="date"
placeholder="请选择开始时间"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="预约结束时间" prop="endTime">
<el-date-picker clearable v-model="queryParams.endTime" type="date" placeholder="请选择结束时间" value-format="YYYY-MM-DD" />
<el-date-picker
clearable
v-model="queryParams.endTime"
type="date"
placeholder="请选择结束时间"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="预约状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择预约状态" clearable>
<el-select v-model="queryParams.status" placeholder="请选择预约状态" clearable style="width: 100%">
<el-option v-for="dict in hall_reserv_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
@@ -37,14 +63,10 @@
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['hq:hallReservation:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['hq:hallReservation:edit']"
>修改</el-button
>
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['hq:hallReservation:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['hq:hallReservation:remove']"
>删除</el-button
>
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['hq:hallReservation:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Download" @click="handleExport" v-hasPermi="['hq:hallReservation:export']">导出</el-button>
@@ -53,7 +75,14 @@
</el-row>
</template>
<el-table v-loading="loading" border :data="hallReservationList" @selection-change="handleSelectionChange" style="margin-bottom: 0">
<el-table
v-loading="loading"
border
:data="hallReservationList"
@selection-change="handleSelectionChange"
style="margin-bottom: 0"
@row-dblclick="handleView"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="报告厅ID" align="center" prop="hallId" />
<el-table-column label="报告厅名称" align="center" prop="hallName" />
@@ -78,13 +107,9 @@
:hide-after="100"
effect="dark"
>
<span class="purpose-text">
{{
scope.row.reservationPurpose && scope.row.reservationPurpose.length > 100
? scope.row.reservationPurpose.substring(0, 100) + '...'
: scope.row.reservationPurpose
}}
</span>
<div class="purpose-text">
{{ scope.row.reservationPurpose }}
</div>
</el-tooltip>
</template>
</el-table-column>
@@ -110,17 +135,13 @@
:hide-after="100"
effect="dark"
>
<span class="audit-remark-text">
{{
scope.row.auditRemark && scope.row.auditRemark.length > 100
? scope.row.auditRemark.substring(0, 100) + '...'
: scope.row.auditRemark
}}
</span>
<div class="audit-remark-text">
{{ scope.row.auditRemark }}
</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220" fixed="right">
<template #default="scope">
<!-- 查看按钮,所有记录都可以查看 -->
<el-tooltip content="查看" placement="top">
@@ -186,7 +207,7 @@
<span class="hall-id">(ID: {{ hall.id }})</span>
</div>
<div class="hall-description" v-if="hall.hallDescription">
{{ hall.hallDescription }}
{{ displayHallDescription(hall.hallDescription) }}
</div>
<div class="hall-images">
<template v-if="getHallImages(hall.hallImage || '').length > 0">
@@ -206,9 +227,9 @@
</el-select>
</el-form-item>
<!-- 报告厅图片展示区域 -->
<el-form-item label="报告厅图片" v-if="selectedHallImages.length > 0">
<el-form-item label="报告厅图片">
<div class="selected-hall-images">
<div class="image-gallery">
<div class="image-gallery" v-if="selectedHallImages.length > 0">
<div v-for="(image, index) in selectedHallImages" :key="index" class="image-item" @click="previewImage(image, index)">
<img :src="image" class="gallery-image" @error="handleImageError" />
<div class="image-overlay">
@@ -216,6 +237,10 @@
</div>
</div>
</div>
<div v-else class="no-images-placeholder">
<el-icon><Picture /></el-icon>
<span>暂无图片</span>
</div>
</div>
</el-form-item>
<el-form-item label="预约开始时间" prop="startTime">
@@ -226,6 +251,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择预约开始时间"
style="width: 100%"
@change="handleStartTimeChange"
>
</el-date-picker>
</el-form-item>
@@ -269,9 +295,9 @@
<span class="form-text">{{ viewForm.hallName }}</span>
</el-form-item>
<!-- 报告厅图片展示区域 -->
<el-form-item label="报告厅图片" v-if="viewHallImages.length > 0">
<el-form-item label="报告厅图片">
<div class="view-hall-images">
<div class="image-gallery">
<div class="image-gallery" v-if="viewHallImages.length > 0">
<div v-for="(image, index) in viewHallImages" :key="index" class="image-item" @click="previewImage(image, index)">
<img :src="image" class="gallery-image" @error="handleImageError" />
<div class="image-overlay">
@@ -279,6 +305,10 @@
</div>
</div>
</div>
<div v-else class="no-images-placeholder">
<el-icon><Picture /></el-icon>
<span>暂无图片</span>
</div>
</div>
</el-form-item>
<el-form-item label="预约开始时间">
@@ -301,6 +331,19 @@
<el-form-item label="预约状态">
<dict-tag :options="hall_reserv_status" :value="viewForm.status" />
</el-form-item>
<!-- 当状态为审核中时显示可审核人员列表 -->
<el-form-item label="可审核人员" v-if="viewForm.status === '0' && auditorList.length > 0">
<div class="auditor-list">
<div v-for="auditor in auditorList" :key="auditor.userId" class="auditor-item">
<el-avatar :size="32" :src="auditor.avatar">{{ auditor.nickName?.substring(0, 1) }}</el-avatar>
<div class="auditor-info">
<div class="auditor-name">{{ auditor.nickName || auditor.userName }}</div>
<div class="auditor-dept">{{ auditor.dept?.deptName }}</div>
</div>
</div>
<div v-if="auditorList.length === 0" class="no-auditor">暂无可审核人员</div>
</div>
</el-form-item>
<el-form-item label="审核人" v-if="viewForm.auditName">
<span class="form-text">{{ viewForm.auditName }}</span>
</el-form-item>
@@ -325,7 +368,7 @@
<script setup name="HallReservation" lang="ts">
import { listHallReservation, getHallReservation, delHallReservation, addHallReservation, updateHallReservation } from '@/api/hq/hallReservation';
import { listHallInfo } from '@/api/hq/hallInfo';
import { getUserProfile } from '@/api/system/user';
import { getUserProfile, listUsersByPermission } from '@/api/system/user';
import { HallReservationVO, HallReservationQuery, HallReservationForm } from '@/api/hq/hallReservation/types';
import { HallInfoVO } from '@/api/hq/hallInfo/types';
import { useUserStore } from '@/store/modules/user';
@@ -375,6 +418,44 @@ const viewForm = ref<HallReservationForm>({
auditRemark: undefined
});
// 添加审核人员列表
const auditorList = ref<any[]>([]);
/** 获取具有审核权限的人员列表 */
const getAuditorList = async () => {
try {
const res = await listUsersByPermission('hq:hallAudit:list');
auditorList.value = res.data || [];
console.log('审核人员列表:', auditorList.value);
} catch (error) {
console.error('获取审核人员列表失败:', error);
auditorList.value = [];
}
};
/** 验证结束时间 */
const validateEndTime = (rule: any, value: any, callback: any) => {
if (value && form.value.startTime) {
const startTime = new Date(form.value.startTime).getTime();
const endTime = new Date(value).getTime();
if (endTime <= startTime) {
callback(new Error('结束时间必须晚于开始时间'));
} else {
callback();
}
} else {
callback();
}
};
/** 开始时间变化时触发结束时间验证 */
const handleStartTimeChange = () => {
// 如果结束时间已经填写,则触发验证
if (data.form.endTime) {
hallReservationFormRef.value?.validateField('endTime');
}
};
const initFormData: HallReservationForm = {
id: undefined,
hallId: undefined,
@@ -390,6 +471,7 @@ const initFormData: HallReservationForm = {
auditTime: undefined,
auditRemark: undefined
};
const data = reactive<PageData<HallReservationForm, HallReservationQuery>>({
form: { ...initFormData },
queryParams: {
@@ -409,7 +491,25 @@ const data = reactive<PageData<HallReservationForm, HallReservationQuery>>({
rules: {
hallName: [{ required: true, message: '报告厅名称不能为空', trigger: 'change' }],
startTime: [{ required: true, message: '预约开始时间不能为空', trigger: 'blur' }],
endTime: [{ required: true, message: '预约结束时间不能为空', trigger: 'blur' }],
endTime: [
{ required: true, message: '预约结束时间不能为空', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (value && data.form.startTime) {
const startTime = new Date(data.form.startTime).getTime();
const endTime = new Date(value).getTime();
if (endTime <= startTime) {
callback(new Error('结束时间必须晚于开始时间'));
} else {
callback();
}
} else {
callback();
}
},
trigger: 'blur'
}
],
reservationPurpose: [{ required: true, message: '预约用途不能为空', trigger: 'blur' }]
}
});
@@ -635,6 +735,11 @@ const handleView = async (row: HallReservationVO) => {
const res = await getHallReservation(row.id);
Object.assign(viewForm.value, res.data);
// 如果状态是审核中,获取审核人员列表
if (viewForm.value.status === '0') {
await getAuditorList();
}
// 查找对应的报告厅并显示图片
if (viewForm.value.hallName) {
const selectedHall = hallList.value.find((hall) => hall.hallName === viewForm.value.hallName);
@@ -747,6 +852,13 @@ const handleExport = () => {
);
};
// 在 <script setup> 部分适当位置添加工具函数
/** 将文本截断到指定长度(默认20个字符),超出部分以省略号结尾 */
const displayHallDescription = (desc: string, maxLen = 20) => {
if (!desc) return '';
return desc.length > maxLen ? desc.slice(0, maxLen) + '...' : desc;
};
onMounted(() => {
getList();
getHallList();
@@ -808,19 +920,19 @@ onMounted(() => {
font-size: 12px;
color: #606266;
margin-bottom: 4px;
line-height: 1.3;
max-height: 36px;
max-width: 240px; /* 新增:限制最大宽度,防止下拉框过宽 */
overflow: hidden;
white-space: nowrap; /* 单行显示 */
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: block;
}
/* 下拉框图片容器:每行最多 6 张图片 */
.hall-images {
display: flex;
display: grid;
grid-template-columns: repeat(6, 40px); /* 6 列,每列宽 40px */
gap: 4px;
flex-wrap: wrap;
}
.hall-image {
@@ -849,22 +961,28 @@ onMounted(() => {
/* 表格中预约用途文本样式 */
.purpose-text {
display: inline-block;
max-width: 100%;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
word-break: break-all;
word-wrap: break-word;
line-height: 1.4;
cursor: pointer;
max-height: 4.2em;
}
/* 表格中审核备注文本样式 */
.audit-remark-text {
display: inline-block;
max-width: 100%;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
word-break: break-all;
word-wrap: break-word;
line-height: 1.4;
cursor: pointer;
max-height: 4.2em;
}
/* 查看详情表单样式 */
@@ -923,6 +1041,7 @@ onMounted(() => {
display: flex;
flex-wrap: wrap;
gap: 12px;
max-width: 450px; /* 与预约用途保持一致 */
}
.image-item {
@@ -970,6 +1089,88 @@ onMounted(() => {
color: white;
font-size: 20px;
}
/* 暂无图片占位样式 */
.no-images-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
max-width: 450px; /* 与预约用途保持一致 */
height: 120px;
background-color: #f5f7fa;
border: 2px dashed #dcdfe6;
border-radius: 8px;
color: #909399;
}
.no-images-placeholder .el-icon {
font-size: 32px;
margin-bottom: 8px;
}
.no-images-placeholder span {
font-size: 14px;
}
/* 审核人员列表样式 */
.auditor-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 8px 0;
}
.auditor-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background-color: #f5f7fa;
border-radius: 8px;
transition: all 0.3s ease;
}
.auditor-item:hover {
background-color: #ecf5ff;
transform: translateY(-2px);
}
.auditor-info {
display: flex;
flex-direction: column;
}
.auditor-name {
font-size: 14px;
color: #303133;
font-weight: 500;
}
.auditor-dept {
font-size: 12px;
color: #909399;
}
.no-auditor {
color: #909399;
font-size: 14px;
font-style: italic;
}
/* 页面容器样式 */
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>
<style>
+79 -37
View File
@@ -4,27 +4,44 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<!-- 第一行:4个检索元素 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="类型名称" prop="categoryName">
<el-input v-model="queryParams.categoryName" placeholder="请输入类型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="类型编码" prop="categoryCode">
<el-input v-model="queryParams.categoryCode" placeholder="请输入类型编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="技能等级" prop="skillLevel">
<el-select v-model="queryParams.skillLevel" placeholder="请选择技能等级" clearable>
<el-select v-model="queryParams.skillLevel" placeholder="请选择技能等级" clearable style="width: 100%">
<el-option v-for="dict in pm_skill_level" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 操作按钮行 -->
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
@@ -53,15 +70,17 @@
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
border
header-align="center"
@row-click="handleRowClick"
style="width: 100%"
class="tree-table"
>
<el-table-column prop="categoryName" label="类型名称" min-width="260" show-overflow-tooltip>
<el-table-column align="left" header-align="center" prop="categoryName" label="类型名称" min-width="260" show-overflow-tooltip>
<template #default="scope">
<span class="category-name-cell">
<el-icon v-if="scope.row.iconUrl" class="category-icon" :style="{ color: scope.row.colorCode }">
<!-- <el-icon v-if="scope.row.iconUrl" class="category-icon" :style="{ color: scope.row.colorCode }">
<component :is="getCategoryIcon(scope.row.categoryCode)" />
</el-icon>
</el-icon> -->
<span class="category-name">{{ scope.row.categoryName }}</span>
<el-tag v-if="scope.row.childrenCount > 0" size="small" type="info" class="children-count-tag">
{{ scope.row.childrenCount }}个子类
@@ -69,8 +88,8 @@
</span>
</template>
</el-table-column>
<el-table-column prop="categoryCode" label="类型编码" width="150" show-overflow-tooltip />
<el-table-column prop="categoryDescription" label="类型描述" min-width="200" show-overflow-tooltip />
<el-table-column prop="categoryCode" label="类型编码" width="150" align="left" header-align="center" show-overflow-tooltip />
<el-table-column prop="categoryDescription" label="类型描述" min-width="200" align="left" header-align="center" show-overflow-tooltip />
<el-table-column prop="skillLevel" label="技能等级" width="100" align="center">
<template #default="scope">
<el-tag :type="getSkillLevelTagType(scope.row.skillLevel)" size="small">
@@ -78,7 +97,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="estimatedDuration" label="预估时长" width="120" align="center">
<!-- <el-table-column prop="estimatedDuration" label="预估时长" width="120" align="center">
<template #default="scope">
<span v-if="scope.row.estimatedDuration">{{ formatDuration(scope.row.estimatedDuration) }}</span>
<span v-else>-</span>
@@ -90,7 +109,7 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="usageCount" label="使用次数" width="100" align="center" />
<el-table-column prop="usageCount" label="使用次数" width="100" align="center" /> -->
<el-table-column prop="sortOrder" label="排序" width="80" align="center" />
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="scope">
@@ -170,7 +189,7 @@
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="预估时长" prop="estimatedDuration">
<el-input-number v-model="form.estimatedDuration" :min="0" :max="9999" placeholder="分钟" style="width: 100%" />
@@ -181,13 +200,13 @@
<el-input-number v-model="form.standardCost" :min="0" :precision="2" placeholder="元" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-row> -->
<el-row :gutter="20">
<el-col :span="12">
<!-- <el-col :span="12">
<el-form-item label="分类颜色" prop="colorCode">
<el-color-picker v-model="form.colorCode" :predefine="predefineColors" :clearable="false" style="width: 100%" />
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="12">
<el-form-item label="排序顺序" prop="sortOrder">
<el-input-number v-model="form.sortOrder" :min="0" placeholder="数字越小越靠前" style="width: 100%" />
@@ -231,9 +250,9 @@
<el-descriptions :column="2" border>
<el-descriptions-item label="类型名称">
<div class="category-name-view">
<el-icon v-if="viewData.iconUrl" class="category-icon" :style="{ color: viewData.colorCode }">
<!-- <el-icon v-if="viewData.iconUrl" class="category-icon" :style="{ color: viewData.colorCode }">
<component :is="getCategoryIcon(viewData.categoryCode)" />
</el-icon>
</el-icon> -->
<span class="category-name">{{ viewData.categoryName }}</span>
</div>
</el-descriptions-item>
@@ -244,7 +263,7 @@
{{ getSkillLevelText(viewData.skillLevel) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="预估时长">
<!-- <el-descriptions-item label="预估时长">
{{ viewData.estimatedDuration ? formatDuration(viewData.estimatedDuration) : '-' }}
</el-descriptions-item>
<el-descriptions-item label="标准成本">
@@ -253,7 +272,7 @@
<el-descriptions-item label="使用次数">{{ viewData.usageCount }}</el-descriptions-item>
<el-descriptions-item label="最后使用时间">
{{ viewData.lastUsedTime ? proxy.parseTime(viewData.lastUsedTime, '{y}-{m}-{d} {h}:{i}') : '-' }}
</el-descriptions-item>
</el-descriptions-item> -->
<el-descriptions-item label="排序顺序">{{ viewData.sortOrder }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="viewData.status === '1' ? 'success' : 'danger'" size="small">
@@ -286,6 +305,8 @@ import {
updateRepairCategory,
getRepairCategoryTree
} from '@/api/hq/pm/repairCategory';
import { ref, reactive, getCurrentInstance, nextTick, onMounted, type ComponentInternalInstance } from 'vue';
import type { FormInstance as ElFormInstance, FormRules as ElFormRules } from 'element-plus';
import { RepairCategoryVO, RepairCategoryForm, RepairCategoryQuery } from '@/api/hq/pm/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -296,6 +317,8 @@ const loading = ref(true);
const showSearch = ref(true);
const isExpandAll = ref(false);
const refreshTable = ref(true);
// 标记首次加载,用于控制默认展示主分类树
const initialLoad = ref(true);
// 字典数据
const pm_skill_level = ref([
@@ -363,24 +386,20 @@ const buttonLoading = ref(false);
const getList = async () => {
loading.value = true;
try {
// 根据是否有搜索条件决定使用哪个接口
let res;
const hasSearchConditions =
queryParams.value.categoryName || queryParams.value.categoryCode || queryParams.value.skillLevel || queryParams.value.status;
if (hasSearchConditions) {
// 有搜索条件时使用列表接口,然后手动构建树形结构
res = await listRepairCategory(queryParams.value);
if (res.code === 200 && res.rows) {
categoryList.value = buildTreeFromList(res.rows);
// 首次加载时使用树形接口获取所有分类
if (initialLoad.value) {
const res = await getRepairCategoryTree();
if (res.code === 200 && res.data) {
categoryList.value = res.data;
} else {
categoryList.value = [];
}
initialLoad.value = false; // 首次加载完成后,后续操作都使用列表接口
} else {
// 无搜索条件时直接使用树形接口
res = await getRepairCategoryTree();
if (res.code === 200 && res.data) {
categoryList.value = res.data;
// 后续操作使用列表接口,并根据搜索条件过滤
const res = await listRepairCategory(queryParams.value);
if (res.code === 200 && res.rows) {
categoryList.value = buildTreeFromList(res.rows);
} else {
categoryList.value = [];
}
@@ -468,6 +487,10 @@ const handleUpdate = async (row: RepairCategoryVO) => {
getCategoryTree();
const res = await getRepairCategory(row.id);
Object.assign(form.value, res.data);
// 若后端未返回技能等级,默认设置为中级
if (!form.value.skillLevel) {
form.value.skillLevel = '2';
}
dialog.visible = true;
dialog.title = '修改维修类型';
};
@@ -479,6 +502,11 @@ const handleView = async (row: RepairCategoryVO) => {
viewDialog.visible = true;
};
/** 行点击预览 */
const handleRowClick = (row: RepairCategoryVO) => {
handleView(row);
};
/** 提交按钮 */
const submitForm = () => {
categoryFormRef.value?.validate(async (valid: boolean) => {
@@ -515,13 +543,17 @@ const handleDelete = async (row: RepairCategoryVO) => {
/** 状态修改 */
const handleStatusChange = async (row: RepairCategoryVO) => {
const originalStatus = row.status;
const text = row.status === '1' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要' + text + '"' + row.categoryName + '"类型吗?');
await updateRepairCategory({ id: row.id, status: row.status });
await proxy?.$modal.confirm(`确认要${text}"${row.categoryName}"类型吗?`);
// 移除前端辅助字段,只保留后端需要的核心字段
const { children, ...updateData } = row as any;
await updateRepairCategory(updateData);
proxy?.$modal.msgSuccess(text + '成功');
} catch {
row.status = row.status === '1' ? '0' : '1';
} catch (e) {
// 如果请求失败或被取消,则还原原状态
row.status = originalStatus;
}
};
@@ -543,7 +575,7 @@ const reset = () => {
categoryDescription: '',
parentId: 0, // 使用数字类型,确保与树形选择器数据类型一致
requiredSkills: '',
skillLevel: '',
skillLevel: '2', // 默认中级
estimatedDuration: undefined,
standardCost: undefined,
iconUrl: '',
@@ -840,4 +872,14 @@ onMounted(() => {
:deep(.el-color-picker__color) {
border: 0px !important;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
}
</style>