zzw
6 小时以前 ce841ddf85c48bae32f9e3068403fdfe98ac8711
feat: add extension defaults for fault reporting
13个文件已修改
493 ■■■■■ 已修改文件
HANDOFF.md 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-server/src/main/java/com/emergencyrepair/auth/AuthController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DatabaseBootstrap.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-web/src/views/Login.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-web/src/views/fault/FaultReport.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-web/src/views/system/OrganizationManage.vue 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
emergency-repair-web/src/views/system/UserManage.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
requirement/交接文件_2026.10.3.md 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
requirement/改进20261003.txt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HANDOFF.md
@@ -938,3 +938,57 @@
- ä½¿ç”¨æ–° jar é‡å¯åŽå†æ¬¡æŸ¥è¯¢ï¼Œå››å¼ ä¸šåŠ¡è¡¨ä»å‡ä¸º `0` è¡Œï¼›ç™»å½•接口 `200`,前端首页 `200`。
- å½“前后端 PID `21528`,监听 `8091`;日志:`.codex-run/backend-20261007-cleared.out.log`、`.codex-run/backend-20261007-cleared.err.log`。
- å¦‚需重新灌入演示业务数据,将 `app.seed-demo-data` ä¸´æ—¶è®¾ä¸º `true` åŽé‡å¯ã€‚
## 26. æ”¶å°¾å¿«ç…§ï¼ˆ2026-10-07 å‘¨ä¸‰ï¼Œéœ€æ±‚ 28-30 æ‰©å±•属性与新增故障默认值)
**需求口径**
- éœ€æ±‚ 30 ç¼–号已确认;新增故障里的“责任”就是“负责人”。
- éƒ¨é—¨æ‰©å±•属性“负责rk_T”的值就是 `rk_X号`,值必须是产品结构树 `side` ç±»åž‹èŠ‚ç‚¹ï¼Œå•é€‰ã€‚
- æ–°å¢žæ•…障的 `rk_X号` é»˜è®¤å€¼åªå–登录用户所属部门的扩展属性;即使用户扩展属性也设置了“负责rk_T”,仍以部门设置为准。
- â€œrk_TD专业”与现有“rk_T队专业”是同一字段,代码字段为 `teamMajor`,当前界面暂时沿用“rk_T队专业”。
- å¤–部 `CSICZB_ZB.SYS_DEPT`、`CSICZB_ZB.SYS_USER` ç”±å…¶ä»–系统维护,本项目只读;扩展属性保存到本地新表。
**本轮完成内容**
- `DatabaseBootstrap` è‡ªåŠ¨åˆ›å»º `LX.T_SYS_DEPT_EXT`(`DEPT_ID`、`RESPONSIBLE_X_NO` åŠå®¡è®¡å­—段)和 `LX.T_SYS_USER_EXT`(`USER_ID`、`ENGINEERING_MAJOR`、`TEAM_MAJOR`、`RESPONSIBLE_X_NO` åŠå®¡è®¡å­—段)。
- ç»„织机构页面增加“扩展属性”按钮和弹窗,可设置部门“负责rk_T”;列表显示已设置值。
- ç”¨æˆ·ç®¡ç†é¡µé¢å¢žåŠ â€œæ‰©å±•å±žæ€§â€æŒ‰é’®å’Œå¼¹çª—ï¼Œå¯è®¾ç½®å·¥ç¨‹ä¸“ä¸šã€rk_T队专业、负责rk_T;列表显示扩展属性是否已设置。
- æ–°å¢žæ•…障弹窗默认带出:上报单位为当前登录人的单位、负责人为当前登录人、`rk_X号`为当前部门扩展属性、工程专业和 rk_T队专业为当前用户扩展属性。
- ç”¨æˆ·æ‰©å±•属性为空时,工程专业和 rk_T队专业不再默认选择配置列表第一项。
**接口与权限**
- `GET/PUT /api/system/depts/{deptId}/ext`:查询和保存部门扩展属性。
- `GET/PUT /api/system/users/{userId}/ext`:查询和保存用户扩展属性。
- `/api/auth/login`、`/api/auth/me` è¿”回 `userId`、`deptId`、`engineeringMajor`、`teamMajor`、`deptResponsibleXNo`、`responsibleXNo`。
- æŒ‰é’®æƒé™æ–°å¢ž `system:organization:ext`(扩展属性设置)、`system:users:ext`(扩展属性设置)。
**主要变更文件**
- `emergency-repair-server/src/main/java/com/emergencyrepair/auth/AuthController.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DatabaseBootstrap.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java`
- `emergency-repair-web/src/views/Login.vue`
- `emergency-repair-web/src/views/fault/FaultReport.vue`
- `emergency-repair-web/src/views/system/OrganizationManage.vue`
- `emergency-repair-web/src/views/system/UserManage.vue`
- `requirement/改进20261003.txt`(补充需求 28-30)
**验证结果**
- åŽç«¯ `mvn -B -DskipTests clean package` é€šè¿‡ï¼›å‰ç«¯ `npm run build` é€šè¿‡ï¼Œä»…有既有包体积告警。
- `node .codex-run/verify-req28-30.js` è¿”回 `pass: true`。
- éƒ¨é—¨ `1390477009348739073 / sy区域` è®¾ç½® `rk_X号=201`,用户 `123 / yszl` è®¾ç½®å·¥ç¨‹ä¸“业 `动力专业`、rk_T队专业 `液压专业`、负责 `202`。
- æ–°å¢žæ•…障实际默认值为:上报单位 `sy区域`、负责人 `助理用户(演示)`、`rk_X号=201`、rk_T队专业 `液压专业`、工程专业 `动力专业`。
- æˆªå›¾ï¼š`.codex-run/req28-org-ext.png`、`.codex-run/req29-user-ext.png`、`.codex-run/req30-fault-defaults.png`。
- éªŒè¯åŽå·²æ¸…除临时部门、用户扩展属性,查询确认 `configured=false` ä¸”字段为空。
**当前运行状态(2026-10-07)**
- åŽç«¯ PID `56124`,监听 `http://127.0.0.1:8091`;前端 PID `57312`,监听 `http://localhost:8082`。
- ä¸º UI éªŒè¯å¯åŠ¨çš„ headless Edge PID `95756`,CDP ç«¯å£ `9222`;配置目录 `.codex-run/edge-profile-req28-30`。
- åŽç«¯æ—¥å¿—:`.codex-run/backend-20261007-req28-30-final.out.log`、`.codex-run/backend-20261007-req28-30-final.err.log`。
- ç™»å½• `admin / admin123`;数据库 `SYSDBA/SYSDBA`,业务 schema `LX`。
**接手注意**
- å¼€å‘环境敏感词配置会把 `rk_T` æ˜¾ç¤ºä¸ºâ€œæ¢¯â€ï¼Œå› æ­¤é¡µé¢æˆªå›¾ä¸­å¯èƒ½æ˜¾ç¤ºâ€œè´Ÿè´£æ¢¯â€ï¼›ä»£ç å’ŒåŽç«¯å­—段仍是 `rk_T`。
- ä¸è¦æäº¤ `.codex-run`、Office é”æ–‡ä»¶æˆ– `emergency-repair-web/public/新建 æ–‡æœ¬æ–‡æ¡£.txt`。
- æœ¬è½®ä»£ç ã€éœ€æ±‚文件和交接记录一并提交并推送公司 Git ä»“库 `main` åˆ†æ”¯ã€‚
emergency-repair-server/src/main/java/com/emergencyrepair/auth/AuthController.java
@@ -58,6 +58,12 @@
        data.put("realName", StringUtils.hasText(stringValue(user.get("nickName")))
                ? stringValue(user.get("nickName")) : username);
        data.put("deptName", stringValue(user.get("deptName")));
        data.put("userId", String.valueOf(user.get("userId")));
        data.put("deptId", user.get("deptId") == null ? "" : String.valueOf(user.get("deptId")));
        data.put("engineeringMajor", stringValue(user.get("engineeringMajor")));
        data.put("teamMajor", stringValue(user.get("teamMajor")));
        data.put("deptResponsibleXNo", stringValue(user.get("deptResponsibleXNo")));
        data.put("responsibleXNo", stringValue(user.get("responsibleXNo")));
        data.put("roles", systemService.roleCodes(username));
        data.put("permissions", systemService.permissionCodes(username));
        return Result.ok(data);
@@ -76,6 +82,12 @@
        data.put("realName", StringUtils.hasText(stringValue(user.get("nickName")))
                ? stringValue(user.get("nickName")) : username);
        data.put("deptName", stringValue(user.get("deptName")));
        data.put("userId", String.valueOf(user.get("userId")));
        data.put("deptId", user.get("deptId") == null ? "" : String.valueOf(user.get("deptId")));
        data.put("engineeringMajor", stringValue(user.get("engineeringMajor")));
        data.put("teamMajor", stringValue(user.get("teamMajor")));
        data.put("deptResponsibleXNo", stringValue(user.get("deptResponsibleXNo")));
        data.put("responsibleXNo", stringValue(user.get("responsibleXNo")));
        data.put("roles", systemService.roleCodes(username));
        data.put("permissions", systemService.permissionCodes(username));
        return Result.ok(data);
@@ -87,9 +99,13 @@
        }
        try {
            List<Map<String, Object>> rows = jdbcTemplate.query(
                    "SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.PASSWORD, u.STATUS, d.DEPT_NAME " +
                            "FROM LX.SYS_USER u LEFT JOIN LX.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID " +
                            "WHERE u.USER_NAME = ? AND (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL)",
                    "SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.PASSWORD, u.STATUS, u.DEPT_ID, d.DEPT_NAME, "
                            + "ue.ENGINEERING_MAJOR, ue.TEAM_MAJOR, ue.RESPONSIBLE_X_NO AS USER_RESPONSIBLE_X_NO, "
                            + "de.RESPONSIBLE_X_NO AS DEPT_RESPONSIBLE_X_NO "
                            + "FROM LX.SYS_USER u LEFT JOIN LX.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID "
                            + "LEFT JOIN LX.T_SYS_USER_EXT ue ON ue.USER_ID = u.USER_ID AND (ue.DEL_FLAG = 0 OR ue.DEL_FLAG IS NULL) "
                            + "LEFT JOIN LX.T_SYS_DEPT_EXT de ON de.DEPT_ID = u.DEPT_ID AND (de.DEL_FLAG = 0 OR de.DEL_FLAG IS NULL) "
                            + "WHERE u.USER_NAME = ? AND (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL)",
                    new Object[]{username},
                    (rs, rowNum) -> {
                        Map<String, Object> row = new LinkedHashMap<>();
@@ -98,6 +114,12 @@
                        row.put("nickName", rs.getString("NICK_NAME"));
                        row.put("password", rs.getString("PASSWORD"));
                        row.put("status", rs.getString("STATUS"));
                        Object deptId = rs.getObject("DEPT_ID");
                        row.put("deptId", deptId == null ? null : rs.getLong("DEPT_ID"));
                        row.put("engineeringMajor", rs.getString("ENGINEERING_MAJOR"));
                        row.put("teamMajor", rs.getString("TEAM_MAJOR"));
                        row.put("responsibleXNo", rs.getString("USER_RESPONSIBLE_X_NO"));
                        row.put("deptResponsibleXNo", rs.getString("DEPT_RESPONSIBLE_X_NO"));
                        row.put("deptName", rs.getString("DEPT_NAME"));
                        return row;
                    });
emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DatabaseBootstrap.java
@@ -18,7 +18,7 @@
            "T_BASE_EQUIPMENT", "T_PRODUCT_TREE", "T_WORK_ORDER", "T_COMPLETION_INFO",
            "T_ACCEPTANCE", "T_SYS_MENU", "T_SYS_ROLE", "T_SYS_ROLE_MENU",
            "T_SYS_TENANT_MENU", "T_SYS_ROLE_USER", "T_SYS_LOGIN_LOG", "T_SYS_OPERATION_LOG",
            "T_SYS_ERROR_LOG"
            "T_SYS_ERROR_LOG", "T_SYS_DEPT_EXT", "T_SYS_USER_EXT"
    };
    private final JdbcTemplate jdbcTemplate;
@@ -122,6 +122,8 @@
        ddl.put("T_SYS_ROLE_MENU", "CREATE TABLE LX.T_SYS_ROLE_MENU (ID BIGINT NOT NULL PRIMARY KEY, ROLE_ID BIGINT, MENU_ID BIGINT, CREATE_TIME TIMESTAMP)");
        ddl.put("T_SYS_TENANT_MENU", "CREATE TABLE LX.T_SYS_TENANT_MENU (ID BIGINT NOT NULL PRIMARY KEY, TENANT_ID BIGINT, MENU_ID BIGINT, CREATE_TIME TIMESTAMP)");
        ddl.put("T_SYS_ROLE_USER", "CREATE TABLE LX.T_SYS_ROLE_USER (ID BIGINT NOT NULL PRIMARY KEY, ROLE_ID BIGINT, USER_ID BIGINT, CREATE_TIME TIMESTAMP)");
        ddl.put("T_SYS_DEPT_EXT", "CREATE TABLE LX.T_SYS_DEPT_EXT (DEPT_ID BIGINT NOT NULL PRIMARY KEY, RESPONSIBLE_X_NO VARCHAR(40), CREATE_BY VARCHAR(120), UPDATE_BY VARCHAR(120), DEL_FLAG INT DEFAULT 0, CREATE_TIME TIMESTAMP, UPDATE_TIME TIMESTAMP)");
        ddl.put("T_SYS_USER_EXT", "CREATE TABLE LX.T_SYS_USER_EXT (USER_ID BIGINT NOT NULL PRIMARY KEY, ENGINEERING_MAJOR VARCHAR(100), TEAM_MAJOR VARCHAR(100), RESPONSIBLE_X_NO VARCHAR(40), CREATE_BY VARCHAR(120), UPDATE_BY VARCHAR(120), DEL_FLAG INT DEFAULT 0, CREATE_TIME TIMESTAMP, UPDATE_TIME TIMESTAMP)");
        ddl.put("T_SYS_LOGIN_LOG", "CREATE TABLE LX.T_SYS_LOGIN_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), DEPT_NAME VARCHAR(120), LOGIN_IP VARCHAR(200), STATUS VARCHAR(20), MESSAGE VARCHAR(500), LOGIN_TIME TIMESTAMP)");
        ddl.put("T_SYS_OPERATION_LOG", "CREATE TABLE LX.T_SYS_OPERATION_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), MODULE VARCHAR(100), OPERATION_TYPE VARCHAR(40), TARGET_TYPE VARCHAR(100), TARGET_ID VARCHAR(80), DESCRIPTION VARCHAR(500), REQUEST_METHOD VARCHAR(10), REQUEST_URI VARCHAR(300), RESULT VARCHAR(20), CREATE_TIME TIMESTAMP)");
        ddl.put("T_SYS_ERROR_LOG", "CREATE TABLE LX.T_SYS_ERROR_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), MODULE VARCHAR(100), REQUEST_METHOD VARCHAR(10), REQUEST_URI VARCHAR(500), EXCEPTION_TYPE VARCHAR(300), MESSAGE VARCHAR(2000), STACK_TRACE CLOB, ERROR_TIME TIMESTAMP)");
emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java
@@ -128,6 +128,18 @@
    }
    @Data
    public static class DeptExtRequest {
        private String responsibleXNo;
    }
    @Data
    public static class UserExtRequest {
        private String engineeringMajor;
        private String teamMajor;
        private String responsibleXNo;
    }
    @Data
    public static class ButtonSyncRequest {
        private Long menuId;
        private List<String> codes = new ArrayList<>();
emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java
@@ -80,6 +80,17 @@
        return Result.ok();
    }
    @GetMapping("/users/{userId}/ext")
    public Result<Map<String, Object>> userExt(@PathVariable Long userId) {
        return Result.ok(systemService.userExt(userId));
    }
    @PutMapping("/users/{userId}/ext")
    public Result<Void> saveUserExt(@PathVariable Long userId, @RequestBody Requests.UserExtRequest request) {
        systemService.saveUserExt(userId, request.getEngineeringMajor(), request.getTeamMajor(), request.getResponsibleXNo());
        return Result.ok();
    }
    @PutMapping("/users/roles/batch")
    public Result<Void> saveBatchUserRoles(@RequestBody Requests.BatchUserRoleRequest request) {
        systemService.saveUserRolesBatch(request.getUserIds(), request.getRoleIds(), request.getMode());
@@ -91,6 +102,17 @@
        return Result.ok(systemService.depts());
    }
    @GetMapping("/depts/{deptId}/ext")
    public Result<Map<String, Object>> deptExt(@PathVariable Long deptId) {
        return Result.ok(systemService.deptExt(deptId));
    }
    @PutMapping("/depts/{deptId}/ext")
    public Result<Void> saveDeptExt(@PathVariable Long deptId, @RequestBody Requests.DeptExtRequest request) {
        systemService.saveDeptExt(deptId, request.getResponsibleXNo());
        return Result.ok();
    }
    @GetMapping("/login-logs")
    public Result<Page<SysLoginLog>> loginLogs(@RequestParam(defaultValue = "1") int current,
                                                @RequestParam(defaultValue = "20") int size,
emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java
@@ -84,10 +84,11 @@
        put("/reports/contractors", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa"));
        put("/reports/trend", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa"));
        put("/system/product-tree", def("system:tree:add", "\u65b0\u589e"), def("system:tree:edit", "\u7f16\u8f91"), def("system:tree:delete", "\u5220\u9664"));
        put("/system/organization", def("system:organization:query", "\u67e5\u8be2"), def("system:organization:ext", "\u6269\u5c55\u5c5e\u6027\u8bbe\u7f6e"));
        put("/system/base-equipment", def("system:tree:add", "\u65b0\u589e"), def("system:tree:edit", "\u7f16\u8f91"), def("system:tree:delete", "\u5220\u9664"));
        put("/system/engineering-majors", def("system:major:add", "\u65b0\u589e"), def("system:major:edit", "\u7f16\u8f91"), def("system:major:delete", "\u5220\u9664"));
        put("/system/team-majors", def("system:major:add", "\u65b0\u589e"), def("system:major:edit", "\u7f16\u8f91"), def("system:major:delete", "\u5220\u9664"));
        put("/system/users", def("system:users:query", "\u67e5\u8be2"), def("system:users:assign", "\u89d2\u8272\u6388\u6743"));
        put("/system/users", def("system:users:query", "\u67e5\u8be2"), def("system:users:assign", "\u89d2\u8272\u6388\u6743"), def("system:users:ext", "\u6269\u5c55\u5c5e\u6027\u8bbe\u7f6e"));
        put("/system/roles", def("system:roles:add", "\u65b0\u589e"), def("system:roles:edit", "\u7f16\u8f91"), def("system:roles:delete", "\u5220\u9664"), def("system:roles:assign", "\u7528\u6237\u8bbe\u7f6e"), def("system:roles:permission", "\u6743\u9650\u8bbe\u7f6e"));
        put("/system/menus", def("system:menus:add", "\u65b0\u589e"), def("system:menus:edit", "\u7f16\u8f91"), def("system:menus:delete", "\u5220\u9664"), def("system:menus:buttons", "\u83dc\u5355\u6309\u94ae\u540c\u6b65"));
        put("/system/login-logs", def("system:login-logs:query", "\u67e5\u8be2"), def("system:login-logs:export", "\u5bfc\u51fa"));
emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java
@@ -13,6 +13,7 @@
import com.emergencyrepair.business.mapper.ProductNodeMapper;
import com.emergencyrepair.business.mapper.TeamMajorMapper;
import com.emergencyrepair.common.BusinessException;
import com.emergencyrepair.common.audit.CurrentUserContext;
import com.emergencyrepair.system.entity.SysMenu;
import com.emergencyrepair.system.entity.SysErrorLog;
import com.emergencyrepair.system.entity.SysLoginLog;
@@ -601,8 +602,10 @@
    public Map<String, Object> userPage(String keyword, Long deptId, Long roleId, int current, int size) {
        List<Object> args = new ArrayList<>();
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.DEPT_ID, d.DEPT_NAME, u.PHONENUMBER, u.STATUS, u.LOGIN_DATE ");
        sql.append("SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.DEPT_ID, d.DEPT_NAME, u.PHONENUMBER, u.STATUS, u.LOGIN_DATE, ");
        sql.append("ue.ENGINEERING_MAJOR, ue.TEAM_MAJOR, ue.RESPONSIBLE_X_NO, CASE WHEN ue.USER_ID IS NULL THEN 0 ELSE 1 END AS EXT_CONFIGURED ");
        sql.append("FROM CSICZB_ZB.SYS_USER u LEFT JOIN CSICZB_ZB.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID ");
        sql.append("LEFT JOIN LX.T_SYS_USER_EXT ue ON ue.USER_ID = u.USER_ID AND (ue.DEL_FLAG = 0 OR ue.DEL_FLAG IS NULL) ");
        sql.append("WHERE (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL) ");
        if (StringUtils.hasText(keyword)) {
            sql.append("AND (u.USER_NAME LIKE ? OR u.NICK_NAME LIKE ?) ");
@@ -631,6 +634,10 @@
            row.put("status", rs.getString("STATUS"));
            java.sql.Timestamp loginDate = rs.getTimestamp("LOGIN_DATE");
            row.put("loginDate", loginDate == null ? null : loginDate.toLocalDateTime().toString().replace('T', ' '));
            row.put("engineeringMajor", rs.getString("ENGINEERING_MAJOR"));
            row.put("teamMajor", rs.getString("TEAM_MAJOR"));
            row.put("responsibleXNo", rs.getString("RESPONSIBLE_X_NO"));
            row.put("extConfigured", rs.getInt("EXT_CONFIGURED") == 1);
            row.put("roles", new ArrayList<Map<String, Object>>());
            return row;
        });
@@ -675,7 +682,11 @@
    public List<Map<String, Object>> depts() {
        return jdbcTemplate.query(
                "SELECT DEPT_ID, DEPT_NAME, PARENT_ID, ORDER_NUM, DEPT_TYPE, LEADER, PHONE, STATUS, DEPT_CODE FROM CSICZB_ZB.SYS_DEPT WHERE (DEL_FLAG = '0' OR DEL_FLAG IS NULL) ORDER BY ORDER_NUM, DEPT_ID",
                "SELECT d.DEPT_ID, d.DEPT_NAME, d.PARENT_ID, d.ORDER_NUM, d.DEPT_TYPE, d.LEADER, d.PHONE, d.STATUS, d.DEPT_CODE, "
                        + "e.RESPONSIBLE_X_NO, CASE WHEN e.DEPT_ID IS NULL THEN 0 ELSE 1 END AS EXT_CONFIGURED "
                        + "FROM CSICZB_ZB.SYS_DEPT d "
                        + "LEFT JOIN LX.T_SYS_DEPT_EXT e ON e.DEPT_ID = d.DEPT_ID AND (e.DEL_FLAG = 0 OR e.DEL_FLAG IS NULL) "
                        + "WHERE (d.DEL_FLAG = '0' OR d.DEL_FLAG IS NULL) ORDER BY d.ORDER_NUM, d.DEPT_ID",
                (rs, rowNum) -> {
                    Map<String, Object> row = new LinkedHashMap<>();
                    row.put("id", String.valueOf(rs.getLong("DEPT_ID")));
@@ -688,10 +699,145 @@
                    row.put("phone", rs.getString("PHONE"));
                    row.put("status", rs.getString("STATUS"));
                    row.put("deptCode", rs.getString("DEPT_CODE"));
                    row.put("responsibleXNo", rs.getString("RESPONSIBLE_X_NO"));
                    row.put("extConfigured", rs.getInt("EXT_CONFIGURED") == 1);
                    return row;
                });
    }
    public Map<String, Object> deptExt(Long deptId) {
        List<Map<String, Object>> rows = jdbcTemplate.query(
                "SELECT d.DEPT_NAME, e.RESPONSIBLE_X_NO FROM CSICZB_ZB.SYS_DEPT d "
                        + "LEFT JOIN LX.T_SYS_DEPT_EXT e ON e.DEPT_ID = d.DEPT_ID AND (e.DEL_FLAG = 0 OR e.DEL_FLAG IS NULL) "
                        + "WHERE d.DEPT_ID = ? AND (d.DEL_FLAG = '0' OR d.DEL_FLAG IS NULL)",
                new Object[]{deptId}, (rs, rowNum) -> {
                    Map<String, Object> row = new LinkedHashMap<>();
                    row.put("deptId", String.valueOf(deptId));
                    row.put("deptName", rs.getString("DEPT_NAME"));
                    row.put("responsibleXNo", rs.getString("RESPONSIBLE_X_NO"));
                    row.put("configured", StringUtils.hasText(rs.getString("RESPONSIBLE_X_NO")));
                    return row;
                });
        if (rows.isEmpty()) {
            throw new BusinessException("部门不存在");
        }
        return rows.get(0);
    }
    @Transactional
    public void saveDeptExt(Long deptId, String responsibleXNo) {
        assertExternalDept(deptId);
        String xNo = normalizeExtValue(responsibleXNo);
        assertKnownXNo(xNo);
        String username = CurrentUserContext.get();
        LocalDateTime now = LocalDateTime.now();
        int delFlag = xNo == null ? 1 : 0;
        int updated = jdbcTemplate.update(
                "UPDATE LX.T_SYS_DEPT_EXT SET RESPONSIBLE_X_NO = ?, DEL_FLAG = ?, UPDATE_BY = ?, UPDATE_TIME = ? WHERE DEPT_ID = ?",
                xNo, delFlag, username, now, deptId);
        if (updated == 0) {
            jdbcTemplate.update(
                    "INSERT INTO LX.T_SYS_DEPT_EXT (DEPT_ID, RESPONSIBLE_X_NO, CREATE_BY, UPDATE_BY, DEL_FLAG, CREATE_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?, ?, ?, ?)",
                    deptId, xNo, username, username, delFlag, now, now);
        }
    }
    public Map<String, Object> userExt(Long userId) {
        List<Map<String, Object>> rows = jdbcTemplate.query(
                "SELECT u.USER_NAME, u.NICK_NAME, e.ENGINEERING_MAJOR, e.TEAM_MAJOR, e.RESPONSIBLE_X_NO "
                        + "FROM CSICZB_ZB.SYS_USER u "
                        + "LEFT JOIN LX.T_SYS_USER_EXT e ON e.USER_ID = u.USER_ID AND (e.DEL_FLAG = 0 OR e.DEL_FLAG IS NULL) "
                        + "WHERE u.USER_ID = ? AND (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL)",
                new Object[]{userId}, (rs, rowNum) -> {
                    Map<String, Object> row = new LinkedHashMap<>();
                    row.put("userId", String.valueOf(userId));
                    row.put("userName", rs.getString("USER_NAME"));
                    row.put("nickName", rs.getString("NICK_NAME"));
                    row.put("engineeringMajor", rs.getString("ENGINEERING_MAJOR"));
                    row.put("teamMajor", rs.getString("TEAM_MAJOR"));
                    row.put("responsibleXNo", rs.getString("RESPONSIBLE_X_NO"));
                    boolean configured = StringUtils.hasText(rs.getString("ENGINEERING_MAJOR"))
                            || StringUtils.hasText(rs.getString("TEAM_MAJOR"))
                            || StringUtils.hasText(rs.getString("RESPONSIBLE_X_NO"));
                    row.put("configured", configured);
                    return row;
                });
        if (rows.isEmpty()) {
            throw new BusinessException("用户不存在");
        }
        return rows.get(0);
    }
    @Transactional
    public void saveUserExt(Long userId, String engineeringMajor, String teamMajor, String responsibleXNo) {
        assertExternalUser(userId);
        String engineering = normalizeExtValue(engineeringMajor);
        String team = normalizeExtValue(teamMajor);
        String xNo = normalizeExtValue(responsibleXNo);
        assertKnownEngineeringMajor(engineering);
        assertKnownTeamMajor(team);
        assertKnownXNo(xNo);
        boolean configured = engineering != null || team != null || xNo != null;
        String username = CurrentUserContext.get();
        LocalDateTime now = LocalDateTime.now();
        int updated = jdbcTemplate.update(
                "UPDATE LX.T_SYS_USER_EXT SET ENGINEERING_MAJOR = ?, TEAM_MAJOR = ?, RESPONSIBLE_X_NO = ?, DEL_FLAG = ?, UPDATE_BY = ?, UPDATE_TIME = ? WHERE USER_ID = ?",
                engineering, team, xNo, configured ? 0 : 1, username, now, userId);
        if (updated == 0) {
            jdbcTemplate.update(
                    "INSERT INTO LX.T_SYS_USER_EXT (USER_ID, ENGINEERING_MAJOR, TEAM_MAJOR, RESPONSIBLE_X_NO, CREATE_BY, UPDATE_BY, DEL_FLAG, CREATE_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
                    userId, engineering, team, xNo, username, username, configured ? 0 : 1, now, now);
        }
    }
    private void assertExternalDept(Long deptId) {
        if (deptId == null) {
            throw new BusinessException("请选择部门");
        }
        Integer count = jdbcTemplate.queryForObject(
                "SELECT COUNT(*) FROM CSICZB_ZB.SYS_DEPT WHERE DEPT_ID = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)",
                Integer.class, deptId);
        if (count == null || count == 0) {
            throw new BusinessException("部门不存在");
        }
    }
    private void assertExternalUser(Long userId) {
        if (userId == null) {
            throw new BusinessException("请选择用户");
        }
        Integer count = jdbcTemplate.queryForObject(
                "SELECT COUNT(*) FROM CSICZB_ZB.SYS_USER WHERE USER_ID = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)",
                Integer.class, userId);
        if (count == null || count == 0) {
            throw new BusinessException("用户不存在");
        }
    }
    private void assertKnownXNo(String xNo) {
        if (xNo != null && !productXNumbers().contains(xNo)) {
            throw new BusinessException("rk_X号必须从产品结构树的side节点中选择");
        }
    }
    private void assertKnownEngineeringMajor(String value) {
        if (value != null && listEngineeringMajors().stream().map(EngineeringMajor::getName)
                .filter(StringUtils::hasText).noneMatch(item -> value.equals(item.trim()))) {
            throw new BusinessException("工程专业不在可选范围内");
        }
    }
    private void assertKnownTeamMajor(String value) {
        if (value != null && listTeamMajors().stream().map(TeamMajor::getName)
                .filter(StringUtils::hasText).noneMatch(item -> value.equals(item.trim()))) {
            throw new BusinessException("rk_T队专业不在可选范围内");
        }
    }
    private String normalizeExtValue(String value) {
        return StringUtils.hasText(value) ? value.trim() : null;
    }
    public List<String> userRoleIds(Long userId) {
        if (userId == null) {
            return new ArrayList<>();
emergency-repair-web/src/views/Login.vue
@@ -48,6 +48,12 @@
          sessionStorage.setItem('username', res.data.username)
          sessionStorage.setItem('realName', res.data.realName)
          sessionStorage.setItem('deptName', res.data.deptName || '')
          sessionStorage.setItem('userId', res.data.userId || '')
          sessionStorage.setItem('deptId', res.data.deptId || '')
          sessionStorage.setItem('engineeringMajor', res.data.engineeringMajor || '')
          sessionStorage.setItem('teamMajor', res.data.teamMajor || '')
          sessionStorage.setItem('deptResponsibleXNo', res.data.deptResponsibleXNo || '')
          sessionStorage.setItem('responsibleXNo', res.data.responsibleXNo || '')
          sessionStorage.setItem('roles', JSON.stringify(res.data.roles || []))
          sessionStorage.setItem('permissions', JSON.stringify(res.data.permissions || []))
          this.$router.push('/dashboard')
emergency-repair-web/src/views/fault/FaultReport.vue
@@ -193,22 +193,24 @@
    taskMeta(value) { return statusMeta(taskStatuses, value) },
    yesNo(value) { return Number(value) === 1 ? '\u662f' : '\u5426' },
    loadAccountInfo() {
      if (sessionStorage.getItem('deptName') !== null) return
      api.get('/auth/me').then(res => {
      return api.get('/auth/me').then(res => {
        const info = res.data || {}
        sessionStorage.setItem('realName', info.realName || sessionStorage.getItem('realName') || '')
        sessionStorage.setItem('deptName', info.deptName || '')
        if (this.form && !this.form.id && !this.form.reportUnit) this.form.reportUnit = info.deptName || ''
        sessionStorage.setItem('userId', info.userId || '')
        sessionStorage.setItem('deptId', info.deptId || '')
        sessionStorage.setItem('engineeringMajor', info.engineeringMajor || '')
        sessionStorage.setItem('teamMajor', info.teamMajor || '')
        sessionStorage.setItem('deptResponsibleXNo', info.deptResponsibleXNo || '')
        sessionStorage.setItem('responsibleXNo', info.responsibleXNo || '')
      })
    },
    loadMajors() {
      api.get('/system/team-majors').then(res => {
        this.teamMajors = res.data || []
        if (!this.form.teamMajor && this.teamMajors.length) this.form.teamMajor = this.teamMajors[0].name
      })
      api.get('/system/engineering-majors').then(res => {
        this.engineeringMajors = res.data || []
        if (!this.form.engineeringMajor && this.engineeringMajors.length) this.form.engineeringMajor = this.engineeringMajors[0].name
      })
    },
    loadXNumbers() {
@@ -270,10 +272,10 @@
        faultCode: '',
        reportUnit: sessionStorage.getItem('deptName') || '',
        reportDate: today,
        xNo: '',
        owner: sessionStorage.getItem('realName') || 'rk_T员',
        teamMajor: (this.teamMajors[0] || {}).name || '',
        engineeringMajor: (this.engineeringMajors[0] || {}).name || '',
        xNo: sessionStorage.getItem('deptResponsibleXNo') || '',
        owner: sessionStorage.getItem('realName') || '',
        teamMajor: sessionStorage.getItem('teamMajor') || '',
        engineeringMajor: sessionStorage.getItem('engineeringMajor') || '',
        equipmentIds: '',
        equipmentName: '',
        parentSystem: '',
@@ -290,7 +292,10 @@
        remark: ''
      }
    },
    openCreate() {
    async openCreate() {
      try {
        await this.loadAccountInfo()
      } catch (e) {}
      this.form = this.blankForm()
      this.dialogVisible = true
    },
emergency-repair-web/src/views/system/OrganizationManage.vue
@@ -39,14 +39,40 @@
        </el-table-column>
        <el-table-column prop="leader" label="负责人" width="120"></el-table-column>
        <el-table-column prop="phone" label="联系电话" width="150"></el-table-column>
        <el-table-column prop="responsibleXNo" label="负责rk_T" width="110" align="center">
          <template slot-scope="{ row }">
            <el-tag v-if="row.responsibleXNo" size="mini" type="success">{{ row.responsibleXNo }}</el-tag>
            <span v-else class="muted">未设置</span>
          </template>
        </el-table-column>
        <el-table-column prop="orderNum" label="排序" width="80" align="center"></el-table-column>
        <el-table-column label="状态" width="90" align="center">
          <template slot-scope="{ row }">
            <el-tag size="mini" :type="row.status === '1' ? 'info' : 'success'">{{ row.status === '1' ? '停用' : '正常' }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="120" align="center" fixed="right">
          <template slot-scope="{ row }">
            <el-button type="text" icon="el-icon-setting" v-permission="'system:organization:ext'" @click="openExt(row)">扩展属性</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-dialog :title="'扩展属性设置 - ' + (currentDept.name || '')" :visible.sync="extVisible" width="520px" append-to-body :close-on-click-modal="false">
      <el-form label-width="105px" v-loading="extLoading">
        <el-form-item label="负责rk_T">
          <el-select v-model="extForm.responsibleXNo" clearable filterable placeholder="请选择rk_X号" style="width:100%">
            <el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option>
          </el-select>
          <div class="field-tip">该值新增故障时作为rk_X号默认值,以部门设置为准。</div>
        </el-form-item>
      </el-form>
      <template slot="footer">
        <el-button @click="extVisible = false">取消</el-button>
        <el-button type="primary" :loading="extSaving" @click="saveExt">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script>
@@ -58,7 +84,13 @@
      loading: false,
      rows: [],
      keyword: '',
      deptType: ''
      deptType: '',
      extVisible: false,
      extLoading: false,
      extSaving: false,
      currentDept: {},
      extForm: { responsibleXNo: '' },
      xNumbers: []
    }
  },
  computed: {
@@ -91,13 +123,35 @@
      return walk(this.tree)
    }
  },
  created() { this.load() },
  created() { this.load(); this.loadXNumbers() },
  methods: {
    load() {
      this.loading = true
      api.get('/system/depts').then(res => {
        this.rows = res.data || []
      }).finally(() => { this.loading = false })
    },
    loadXNumbers() {
      api.get('/system/tree/product/x-numbers').then(res => { this.xNumbers = res.data || [] })
    },
    openExt(row) {
      this.currentDept = { ...row }
      this.extForm = { responsibleXNo: row.responsibleXNo || '' }
      this.extVisible = true
      this.extLoading = true
      api.get('/system/depts/' + row.id + '/ext').then(res => {
        const data = res.data || {}
        this.extForm = { responsibleXNo: data.responsibleXNo || '' }
      }).finally(() => { this.extLoading = false })
    },
    saveExt() {
      if (!this.currentDept.id) return
      this.extSaving = true
      api.put('/system/depts/' + this.currentDept.id + '/ext', this.extForm).then(() => {
        this.$message.success('扩展属性设置成功')
        this.extVisible = false
        this.load()
      }).finally(() => { this.extSaving = false })
    },
    buildTree(rows) {
      const nodes = rows.map(row => ({ ...row, children: [] }))
@@ -124,4 +178,6 @@
</script>
<style scoped>
.summary { color: #6d7f91; font-size: 13px; }
.muted { color: #a3afbc; font-size: 12px; }
.field-tip { margin-top: 4px; color: #7b8a99; font-size: 12px; line-height: 18px; }
</style>
emergency-repair-web/src/views/system/UserManage.vue
@@ -39,6 +39,11 @@
            </template>
          </el-table-column>
          <el-table-column prop="phoneNumber" label="手机号" width="130"></el-table-column>
          <el-table-column label="扩展属性" width="100" align="center">
            <template slot-scope="{ row }">
              <el-button type="text" v-permission="'system:users:ext'" @click="openExt(row)">{{ row.extConfigured ? '编辑' : '设置' }}</el-button>
            </template>
          </el-table-column>
          <el-table-column label="状态" width="80" align="center">
            <template slot-scope="{ row }">
              <el-tag size="mini" :type="isDisabled(row) ? 'info' : 'success'">{{ isDisabled(row) ? '停用' : '正常' }}</el-tag>
@@ -99,6 +104,29 @@
          <el-button type="primary" :loading="batchSaving" v-permission="'system:users:assign'" @click="saveBatchRoles">保存</el-button>
        </span>
      </el-dialog>
      <el-dialog :title="'用户扩展属性设置 - ' + ((currentExtUser.nickName || currentExtUser.userName) || '')" :visible.sync="extVisible" width="560px" append-to-body :close-on-click-modal="false">
        <el-form label-width="105px" v-loading="extLoading">
          <el-form-item label="工程专业">
            <el-select v-model="extForm.engineeringMajor" clearable filterable placeholder="请选择工程专业" style="width:100%">
              <el-option v-for="item in engineeringMajors" :key="item.id" :label="item.name" :value="item.name"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="rk_T队专业">
            <el-select v-model="extForm.teamMajor" clearable filterable placeholder="请选择rk_T队专业" style="width:100%">
              <el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="负责rk_T">
            <el-select v-model="extForm.responsibleXNo" clearable filterable placeholder="请选择rk_X号" style="width:100%">
              <el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option>
            </el-select>
          </el-form-item>
        </el-form>
        <template slot="footer">
          <el-button @click="extVisible = false">取消</el-button>
          <el-button type="primary" :loading="extSaving" @click="saveExt">保存</el-button>
        </template>
      </el-dialog>
  </div>
</template>
<script>
@@ -122,12 +150,21 @@
      roles: [],
      selected: null,
      checkedRoleIds: [],
      extVisible: false,
      extLoading: false,
      extSaving: false,
      currentExtUser: {},
      extForm: { engineeringMajor: '', teamMajor: '', responsibleXNo: '' },
      engineeringMajors: [],
      teamMajors: [],
      xNumbers: [],
      filters: { keyword: '', deptId: null, roleId: null }
    }
  },
  created() {
    this.loadRoles()
    this.loadDepts()
    this.loadExtOptions()
    this.load()
  },
  methods: {
@@ -159,6 +196,34 @@
    loadDepts() {
      api.get('/system/depts').then(res => { this.depts = res.data || [] })
    },
    loadExtOptions() {
      api.get('/system/engineering-majors').then(res => { this.engineeringMajors = res.data || [] })
      api.get('/system/team-majors').then(res => { this.teamMajors = res.data || [] })
      api.get('/system/tree/product/x-numbers').then(res => { this.xNumbers = res.data || [] })
    },
    openExt(row) {
      this.currentExtUser = { ...row }
      this.extForm = { engineeringMajor: '', teamMajor: '', responsibleXNo: '' }
      this.extVisible = true
      this.extLoading = true
      api.get('/system/users/' + row.userId + '/ext').then(res => {
        const data = res.data || {}
        this.extForm = {
          engineeringMajor: data.engineeringMajor || '',
          teamMajor: data.teamMajor || '',
          responsibleXNo: data.responsibleXNo || ''
        }
      }).finally(() => { this.extLoading = false })
    },
    saveExt() {
      if (!this.currentExtUser.userId) return
      this.extSaving = true
      api.put('/system/users/' + this.currentExtUser.userId + '/ext', this.extForm).then(() => {
        this.$message.success('扩展属性设置成功')
        this.extVisible = false
        this.load()
      }).finally(() => { this.extSaving = false })
    },
    search() {
      this.current = 1
      this.load()
requirement/½»½ÓÎļþ_2026.10.3.md
@@ -735,3 +735,57 @@
- æ¸…空前:故障 `23`、派工单 `8`、完工信息 `6`、验收 `6`;清空后均 `0`。
- åŽç«¯ `mvn -B -DskipTests clean package` é€šè¿‡ï¼›æ–° jar é‡å¯åŽå››è¡¨ä»å‡ä¸º `0`。
- å½“前后端 PID `21528`,监听 `8091`;前端 PID `57312`,监听 `8082`;后端错误日志为空。
## äºŒåã€2026-10-07 è”调收尾(需求 28-30)
| åºå· | éœ€æ±‚ | çŠ¶æ€ | å®žçŽ°ä¸ŽéªŒè¯ |
| --- | --- | --- | --- |
| 28 | ç»„织机构增加扩展属性设置,设置部门负责rk_T | å·²å®Œæˆ | ç»„织机构列表增加“扩展属性”按钮和弹窗,从产品结构树 `side` èŠ‚ç‚¹ä¸­å•é€‰ `rk_X号`,保存到 `LX.T_SYS_DEPT_EXT`,不修改外部 `SYS_DEPT` |
| 29 | ç”¨æˆ·ç®¡ç†å¢žåŠ æ‰©å±•å±žæ€§è®¾ç½® | å·²å®Œæˆ | å¯è®¾ç½®å·¥ç¨‹ä¸“业、rk_T队专业、负责rk_T,保存到 `LX.T_SYS_USER_EXT`,不修改外部 `SYS_USER`;rk_TD专业与现有 `teamMajor` ä¸ºåŒä¸€å­—段 |
| 30 | æ–°å¢žæ•…障默认值 | å·²å®Œæˆ | ä¸ŠæŠ¥å•位取登录用户单位,负责人取登录用户,`rk_X号`只取部门扩展属性,工程专业和rk_T队专业取用户扩展属性;未配置时留空,不再取列表第一项 |
### 20.1 ç¡®è®¤å£å¾„
- éœ€æ±‚ 30 ç¼–号正确;新增故障里的“责任”就是“负责人”。
- éƒ¨é—¨æ‰©å±•属性“负责rk_T”就是 `rk_X号`,单选;新增故障的 `rk_X号` åªä»¥éƒ¨é—¨è®¾ç½®ä¸ºå‡†ï¼Œå³ä½¿ç”¨æˆ·ä¹Ÿè®¾ç½®äº†â€œè´Ÿè´£rk_T”也不参与该默认值。
- `rk_TD专业`就是现有页面里的“rk_T队专业”,后续暂统一沿用现有显示,字段为 `teamMajor`。
- å¤–部 `CSICZB_ZB.SYS_DEPT`、`CSICZB_ZB.SYS_USER` ç”±å…¶ä»–系统维护,扩展属性均存本地扩展表。
### 20.2 æŽ¥å£ä¸Žæ•°æ®è¡¨
- `LX.T_SYS_DEPT_EXT`:`DEPT_ID`、`RESPONSIBLE_X_NO`、审计字段。
- `LX.T_SYS_USER_EXT`:`USER_ID`、`ENGINEERING_MAJOR`、`TEAM_MAJOR`、`RESPONSIBLE_X_NO`、审计字段。
- `GET/PUT /api/system/depts/{deptId}/ext`:查询、保存部门扩展属性。
- `GET/PUT /api/system/users/{userId}/ext`:查询、保存用户扩展属性。
- `/api/auth/login`、`/api/auth/me`返回用户、部门和专业扩展信息,供新增故障默认值使用。
- æ–°å¢žæŒ‰é’®æƒé™ `system:organization:ext`、`system:users:ext`。
### 20.3 ä¸»è¦å˜æ›´æ–‡ä»¶
- `emergency-repair-server/src/main/java/com/emergencyrepair/auth/AuthController.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DatabaseBootstrap.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java`
- `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java`
- `emergency-repair-web/src/views/Login.vue`
- `emergency-repair-web/src/views/fault/FaultReport.vue`
- `emergency-repair-web/src/views/system/OrganizationManage.vue`
- `emergency-repair-web/src/views/system/UserManage.vue`
- `requirement/改进20261003.txt`、`HANDOFF.md`(补充需求 28-30 å’Œäº¤æŽ¥è®°å½•)
### 20.4 éªŒè¯ç»“æžœ
- åŽç«¯ `mvn -B -DskipTests clean package` é€šè¿‡ï¼›å‰ç«¯ `npm run build` é€šè¿‡ï¼Œä»…有既有包体积告警。
- `node .codex-run/verify-req28-30.js` è¿”回 `pass: true`。
- éƒ¨é—¨ `sy区域` è®¾ç½® `rk_X号=201`;用户 `yszl` è®¾ç½®å·¥ç¨‹ä¸“业 `动力专业`、rk_T队专业 `液压专业`、负责 `202`。
- æ–°å¢žæ•…障默认值实测:上报单位 `sy区域`、负责人 `助理用户(演示)`、`rk_X号=201`、rk_T队专业 `液压专业`、工程专业 `动力专业`。
- æˆªå›¾ï¼š`.codex-run/req28-org-ext.png`、`.codex-run/req29-user-ext.png`、`.codex-run/req30-fault-defaults.png`。
- éªŒè¯åŽå·²æ¸…空临时扩展属性,查询确认 `configured=false` ä¸”字段为空。
### 20.5 æŽ¥æ‰‹æ³¨æ„
- å½“前后端 PID `56124`,监听 `http://127.0.0.1:8091`;前端 PID `57312`,监听 `http://localhost:8082`;DM8 `127.0.0.1:5236`;Edge CDP `9222`。登录 `admin / admin123`。
- å¼€å‘环境敏感词会把 `rk_T` æ˜¾ç¤ºæˆâ€œæ¢¯â€ï¼Œæˆªå›¾ä¸­çš„“负责梯”对应代码/后端字段 `rk_T`。
- `.codex-run`、Office é”æ–‡ä»¶åŠ `emergency-repair-web/public/新建 æ–‡æœ¬æ–‡æ¡£.txt` ä»ä¸ºæœ¬åœ°ä¸´æ—¶æ–‡ä»¶ï¼Œä¸åº”提交。
- æœ¬è½®ä»£ç ã€éœ€æ±‚文件和交接记录一并提交并推送公司 Git ä»“库 `main` åˆ†æ”¯ã€‚
requirement/¸Ä½ø20261003.txt
@@ -40,3 +40,13 @@
26、日志管理的日期组件2个日期中间那个“至”显示不完整
27、新增故障那里选了rk_X号(对应产品结构树的side类型)后,再选设备的时候还是显示整个产品结构树,应该显示锁选节点的下级节点
28,组织机构(部门树表),增加扩展属性设置,目前主要包括负责rk_T
29,用户管理,增加扩展属性设置,目前主要有工程专业、rk_TD专业,负责rk_T
30、新增故障,默认值,故障上报单位默认填写当前登录人的单位,责任默认填写当前登录人,rk_X号默认填写当前登录人的单位的扩张属性负责rk_T,工程专业、rk_TD专业分别为当前用户的扩展属性工程专业、rk_TD专业
第三条应该编号为 30 å§ï¼Ÿå¦å¤–“责任”是否就是“负责人”?是的
“负责rk_T”具体是选 rk_X号,还是选某个组织/专业?单选还是多选?如果部门和用户都设置了,rk_X号是否只以部门设置为准?选 rk_X号,要求扩展属性和故障表里存的一致,单选,选部门的
你说的“rk_TD专业”是否就是现有页面里的“rk_T队专业”?后续统一显示哪一个名称?是的
扩展属性建议新建本地扩展表保存,例如部门扩展表和用户扩展表,不直接修改外部来源的 SYS_DEPT、SYS_USER è¡¨ã€‚是否按这个方式实现?是的,因为那2个表是别的系统维护的