zlwu 1 год назад
Родитель
Сommit
0869a4adf3

+ 50 - 0
.cursor/cursorrules-backend.md

@@ -1979,3 +1979,53 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
     Page<SysUserVo> selectUserPage(Page<SysUser> page, @Param("bo") SysUserBo bo);
 }
 ```
+
+### 🗑️ 逻辑删除规则(全局强制,2025-07-11新增)
+
+> **适用范围**:所有业务表、所有MyBatis-Plus实体、所有Service删除操作
+>
+> **设计目标**:统一软删除实现,避免物理删除带来的数据丢失,保证查询接口自动过滤已删除数据。
+
+1. **数据库字段**
+   - 字段名:`deleted`  
+   - 字段类型:`tinyint(1)`  
+   - 默认值:`0`(存在)  
+   - 含义:`0`-存在,`1`-已删除
+
+2. **实体类注解**
+   ```java
+   /**
+    * 删除标志(0代表存在 1代表删除)
+    */
+   @TableLogic(value = "0", delval = "1")
+   private Integer deleted;
+   ```
+   - `@TableLogic` 由 MyBatis-Plus 提供,可自动将 `DELETE` SQL 转换为
+     ```sql
+     UPDATE table SET deleted = 1 WHERE id = ?
+     ```
+   - 同时在所有自动生成的 `SELECT` 语句中追加条件 `deleted = 0`,无需显式编写过滤条件。
+
+3. **删除操作代码规范**
+   - Service 层推荐使用 MyBatis-Plus 的 `deleteById` / `deleteBatchIds` 等方法即可触发逻辑删除。
+   - 如需自定义删除 SQL,**必须**包含 `deleted = 0` 过滤并在删除时设置 `deleted = 1`。
+   - Controller 层仍按业务语义暴露 `DELETE /{id}` 接口,无需额外逻辑,保持与物理删除一致的调用方式。
+
+4. **查询操作规范**
+   - 默认情况下,MyBatis-Plus 已自动附加 `deleted = 0` 条件。编写自定义 SQL 时,**必须**手动加上 `AND deleted = 0`。
+   - 对外返回的分页/列表接口,不得包含 `deleted = 1` 的数据。
+
+5. **数据恢复**
+   - 若业务需要"恢复已删除数据",可单独提供接口,将 `deleted` 字段重新置回 `0`。
+
+> 📌 **注意**:逻辑删除是项目级强制要求,任何物理删除(`DELETE FROM ...`)将被代码审查拒绝。
+
+#### 🔴 AI 开发强制要求
+
+当使用 AI 进行数据库开发时:
+
+1. **必须先阅读** `docs/设计文档/database/mysql-database-standard.md` 规范
+2. **必须使用标准模板**,不得随意修改基础字段
+3. **必须保持与现有表结构的一致性**
+4. **建表后必须立即更新** `hzdyjs_xg.sql` 文件
+5. **必须在变更记录中记录** 数据库变更

+ 5 - 0
backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/domain/HqHallInfo.java

@@ -49,5 +49,10 @@ public class HqHallInfo extends TenantEntity {
      */
     private String hallDescription;
 
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic(value = "0", delval = "1")
+    private Integer deleted;
 
 }

+ 6 - 0
backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/domain/HqPmRepairCategory.java

@@ -121,4 +121,10 @@ public class HqPmRepairCategory extends TenantEntity {
      */
     private String remark;
 
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic(value = "0", delval = "1")
+    private Integer deleted;
+
 }

+ 2 - 1
backend/ruoyi-modules/ruoyi-hq/src/main/java/org/dromara/hq/service/impl/HqPmRepairCategoryServiceImpl.java

@@ -254,7 +254,8 @@ public class HqPmRepairCategoryServiceImpl implements IHqPmRepairCategoryService
     @Override
     public List<HqPmRepairCategoryVo> queryTree() {
         LambdaQueryWrapper<HqPmRepairCategory> lqw = Wrappers.lambdaQuery();
-        lqw.eq(HqPmRepairCategory::getStatus, "1");
+        // 不过滤状态,返回所有分类(包括停用状态),前端可自行根据 status 进行展示控制
+        // lqw.eq(HqPmRepairCategory::getStatus, "1");
         lqw.orderByAsc(HqPmRepairCategory::getSortOrder).orderByAsc(HqPmRepairCategory::getId);
         List<HqPmRepairCategoryVo> allList = baseMapper.selectVoList(lqw);
 

+ 1 - 1
web/src/views/hq/pm/repairCategory/index.vue

@@ -71,7 +71,7 @@
           :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
           border
           header-align="center"
-          @row-click="handleRowClick"
+          @row-dblclick="handleRowClick"
           style="width: 100%"
           class="tree-table"
         >

+ 14 - 2
web/src/views/hq/pm/repairOrder/index.vue

@@ -1510,6 +1510,16 @@ const satisfactionLevelOptions = ref([
   { value: '5', label: '非常不满意' }
 ]);
 
+// 新增:过滤已删除(deleted = 1)及已停用(status = 0) 的维修类型
+const filterActiveCategories = (categories: RepairCategoryVO[]): RepairCategoryVO[] => {
+  return (categories || [])
+    .filter((cat: any) => String(cat.deleted) !== '1' && String(cat.status) !== '0')
+    .map((cat) => ({
+      ...cat,
+      children: cat.children ? filterActiveCategories(cat.children) : []
+    }));
+};
+
 /** 查询报修工单列表 */
 const getList = async () => {
   loading.value = true;
@@ -1562,7 +1572,8 @@ const getStatistics = async () => {
 const getCategoryOptions = async () => {
   try {
     const res = await getRepairCategoryOptions();
-    categoryOptions.value = res.data;
+    // 过滤掉已删除或停用的数据
+    categoryOptions.value = (res.data || []).filter((cat: any) => String(cat.deleted) !== '1' && String(cat.status) !== '0');
   } catch (error) {
     console.error('获取维修类型选项失败:', error);
   }
@@ -1572,7 +1583,8 @@ const getCategoryOptions = async () => {
 const getCategoryTreeOptions = async () => {
   try {
     const res = await getRepairCategoryTree();
-    categoryTreeOptions.value = res.data || [];
+    // 过滤掉已删除或停用的数据(递归)
+    categoryTreeOptions.value = filterActiveCategories(res.data || []);
   } catch (error) {
     console.error('获取维修类型树形结构失败:', error);
     categoryTreeOptions.value = [];