diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql index cc2d3d57..0fdb9ad1 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql @@ -263,8 +263,8 @@ CREATE TABLE workcase.tb_workcase( type VARCHAR(50) NOT NULL, -- 故障类型 device VARCHAR(50) DEFAULT NULL, -- 设备名称 device_code VARCHAR(50) DEFAULT NULL, -- 设备代码 - device_name_plate VARCHAR(50) DEFAULT NULL, -- 设备名称牌 - device_name_plate_img VARCHAR(50) NOT NULL, -- 设备名称牌图片 + device_name_plate VARCHAR(50) NOT NULL, -- 设备名称牌 + device_name_plate_img VARCHAR(50) DEFAULT NULL, -- 设备名称牌图片 address VARCHAR(1000) DEFAULT NULL, -- 现场地址 description VARCHAR(1000) DEFAULT NULL, -- 故障描述 imgs VARCHAR(50)[] DEFAULT '{}', -- 工单图片id diff --git a/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginParam.java b/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginParam.java index cbed0cbf..f1c74630 100644 --- a/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginParam.java +++ b/urbanLifelineServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginParam.java @@ -120,4 +120,6 @@ public class LoginParam implements Serializable { */ private String iv; + private Boolean mockMode; + } diff --git a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/AesEncryptUtil.java b/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/AesEncryptUtil.java index eccf20ef..c841903a 100644 --- a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/AesEncryptUtil.java +++ b/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/AesEncryptUtil.java @@ -224,7 +224,7 @@ public class AesEncryptUtil { public static void main(String[] args) { AesEncryptUtil aesEncryptUtil = new AesEncryptUtil("MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="); - String phone = "17857100376"; + String phone = "15170037929"; // 测试加密(每次都不同,不能用于查询) String encryptedPhone1 = aesEncryptUtil.encryptPhone(phone); diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java index 1744dbaa..b3137c12 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java @@ -127,64 +127,94 @@ public class GuestController { // ========================= 微信小程序用户识别登录 ========================= + /** + * 模拟用户数据(用于测试,没有手机号校验appid时使用) + * 格式:手机号 -> [姓名, 微信ID, 角色] + */ + private static final java.util.Map MOCK_USERS = new java.util.HashMap() {{ + put("17857100375", new String[]{"超级管理员", "17857100375", "admin"}); + put("13870055185", new String[]{"魏瑶", "75719954", "engineer"}); + put("15170466624", new String[]{"刘杰", "liujie1370984851", "engineer"}); + put("15170037929", new String[]{"万家明", "WJM15170037929", "engineer"}); + put("19100185270", new String[]{"戴斌", "BaiBin0714", "guest"}); + put("15797790517", new String[]{"余其跃", "a540378218", "guest"}); + put("15879126468", new String[]{"李小华", "wxid_vgmmzfdcwx9021", "guest"}); + }}; + @Operation(summary = "微信小程序用户识别登录") @PostMapping("/identify") public ResultDomain identifyUser(@RequestBody LoginParam loginParam, HttpServletRequest request) { - logger.info("微信小程序登录请求: wechatId={}, code={}, phoneCode={}", + logger.info("微信小程序登录请求: wechatId={}, phone={}, mockMode={}", loginParam.getWechatId(), - loginParam.getCode() != null ? loginParam.getCode().substring(0, Math.min(10, loginParam.getCode().length())) + "..." : null, - loginParam.getPhoneCode() != null ? "有" : "无"); + loginParam.getPhone(), + loginParam.getMockMode() != null ? loginParam.getMockMode() : false); - // 1. 处理微信登录code,获取openid - String openid = null; - String sessionKey = null; - if (loginParam.getCode() != null && !loginParam.getCode().trim().isEmpty()) { - ResultDomain sessionResult = weChatMiniProgramService.code2Session(loginParam.getCode()); - if (sessionResult.getSuccess() && sessionResult.getData() != null) { - openid = sessionResult.getData().getOpenid(); - sessionKey = sessionResult.getData().getSessionKey(); - logger.info("获取openid成功: {}", openid); - // 使用openid作为wechatId - loginParam.setWechatId(openid); + // ========== 模拟模式:直接使用传入的手机号匹配用户 ========== + if (Boolean.TRUE.equals(loginParam.getMockMode()) && loginParam.getPhone() != null) { + String phone = loginParam.getPhone().trim(); + String[] mockUser = MOCK_USERS.get(phone); + + if (mockUser != null) { + // 设置模拟用户信息 + loginParam.setUsername(mockUser[0]); + loginParam.setWechatId(mockUser[1]); + logger.info("模拟登录: phone={}, name={}, wechatId={}, role={}", + phone, mockUser[0], mockUser[1], mockUser[2]); } else { - logger.warn("获取openid失败: {}", sessionResult.getMessage()); + return ResultDomain.failure("未找到该手机号对应的测试用户"); } - } - - // 2. 处理手机号授权 - String phoneNumber = null; - - // 方式1:使用phoneCode获取手机号(新版API,推荐) - if (loginParam.getPhoneCode() != null && !loginParam.getPhoneCode().trim().isEmpty()) { - ResultDomain phoneResult = weChatMiniProgramService.getPhoneNumber(loginParam.getPhoneCode()); - if (phoneResult.getSuccess() && phoneResult.getData() != null && phoneResult.getData().getPhoneInfo() != null) { - phoneNumber = phoneResult.getData().getPhoneInfo().getPurePhoneNumber(); - if (phoneNumber == null) { - phoneNumber = phoneResult.getData().getPhoneInfo().getPhoneNumber(); + } else { + // ========== 正常模式:通过微信API获取手机号 ========== + // 1. 处理微信登录code,获取openid + String openid = null; + String sessionKey = null; + if (loginParam.getCode() != null && !loginParam.getCode().trim().isEmpty()) { + ResultDomain sessionResult = weChatMiniProgramService.code2Session(loginParam.getCode()); + if (sessionResult.getSuccess() && sessionResult.getData() != null) { + openid = sessionResult.getData().getOpenid(); + sessionKey = sessionResult.getData().getSessionKey(); + logger.info("获取openid成功: {}", openid); + loginParam.setWechatId(openid); + } else { + logger.warn("获取openid失败: {}", sessionResult.getMessage()); } - logger.info("通过phoneCode获取手机号成功: {}", phoneNumber); - } else { - logger.warn("通过phoneCode获取手机号失败: {}", phoneResult.getMessage()); } - } - - // 方式2:使用encryptedData和iv解密手机号(旧版API) - if (phoneNumber == null && sessionKey != null - && loginParam.getEncryptedData() != null && !loginParam.getEncryptedData().trim().isEmpty() - && loginParam.getIv() != null && !loginParam.getIv().trim().isEmpty()) { - ResultDomain decryptResult = weChatMiniProgramService.decryptPhoneNumber( - sessionKey, loginParam.getEncryptedData(), loginParam.getIv()); - if (decryptResult.getSuccess() && decryptResult.getData() != null) { - phoneNumber = decryptResult.getData(); - logger.info("通过解密获取手机号成功: {}", phoneNumber); - } else { - logger.warn("解密手机号失败: {}", decryptResult.getMessage()); + + // 2. 处理手机号授权 + String phoneNumber = null; + + // 方式1:使用phoneCode获取手机号(新版API,推荐) + if (loginParam.getPhoneCode() != null && !loginParam.getPhoneCode().trim().isEmpty()) { + ResultDomain phoneResult = weChatMiniProgramService.getPhoneNumber(loginParam.getPhoneCode()); + if (phoneResult.getSuccess() && phoneResult.getData() != null && phoneResult.getData().getPhoneInfo() != null) { + phoneNumber = phoneResult.getData().getPhoneInfo().getPurePhoneNumber(); + if (phoneNumber == null) { + phoneNumber = phoneResult.getData().getPhoneInfo().getPhoneNumber(); + } + logger.info("通过phoneCode获取手机号成功: {}", phoneNumber); + } else { + logger.warn("通过phoneCode获取手机号失败: {}", phoneResult.getMessage()); + } + } + + // 方式2:使用encryptedData和iv解密手机号(旧版API) + if (phoneNumber == null && sessionKey != null + && loginParam.getEncryptedData() != null && !loginParam.getEncryptedData().trim().isEmpty() + && loginParam.getIv() != null && !loginParam.getIv().trim().isEmpty()) { + ResultDomain decryptResult = weChatMiniProgramService.decryptPhoneNumber( + sessionKey, loginParam.getEncryptedData(), loginParam.getIv()); + if (decryptResult.getSuccess() && decryptResult.getData() != null) { + phoneNumber = decryptResult.getData(); + logger.info("通过解密获取手机号成功: {}", phoneNumber); + } else { + logger.warn("解密手机号失败: {}", decryptResult.getMessage()); + } + } + + // 设置手机号 + if (phoneNumber != null) { + loginParam.setPhone(phoneNumber); } - } - - // 设置手机号 - if (phoneNumber != null) { - loginParam.setPhone(phoneNumber); } // 验证参数:必须有wechatId或phone diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java index c6903cdd..1890b2ac 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseController.java @@ -87,6 +87,18 @@ public class WorkcaseController { return workcaseService.deleteWorkcase(workcase); } + @Operation(summary = "撤销工单") + @PreAuthorize("hasAuthority('workcase:ticket:update')") + @PostMapping("/revoke/{workcaseId}") + public ResultDomain revokeWorkcase(@PathVariable(value = "workcaseId") String workcaseId) { + // 创建撤销处理过程 + TbWorkcaseProcessDTO process = new TbWorkcaseProcessDTO(); + process.setWorkcaseId(workcaseId); + process.setAction("repeal"); + process.setMessage("用户撤销工单"); + return workcaseService.createWorkcaseProcess(process); + } + @Operation(summary = "获取工单详情") @PreAuthorize("hasAuthority('workcase:ticket:view')") @GetMapping("/{workcaseId}") diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/VideoMeetingServiceImpl.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/VideoMeetingServiceImpl.java index 128f9056..2bf8207b 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/VideoMeetingServiceImpl.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/VideoMeetingServiceImpl.java @@ -376,6 +376,7 @@ public class VideoMeetingServiceImpl implements VideoMeetingService { meeting.setJwtToken(userJwtToken); meeting.setJitsiIframeUrl(jitsiIframeUrl); // 真正的Jitsi URL meeting.setIframeUrl(meetingPageUrl); // 会议页面URL(用于router跳转) + meeting.setJitsiServerUrl(jitsiProperties.getServer().getUrl()); // 使用当前配置的服务器URL logger.info("生成用户专属会议URL成功: meetingId={}, userId={}, status={}", meetingId, userId, meeting.getStatus()); diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseServiceImpl.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseServiceImpl.java index 476084b2..ccb9b985 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseServiceImpl.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseServiceImpl.java @@ -416,10 +416,38 @@ public class WorkcaseServiceImpl implements WorkcaseService { // 不影响工单完成流程,只记录错误日志 } } else if (WorkcaseProcessAction.REPEAL.getName().equals(action)) { + // 1. 更新工单状态为已撤销 TbWorkcaseDTO workcase = new TbWorkcaseDTO(); workcase.setWorkcaseId(workcaseProcess.getWorkcaseId()); workcase.setStatus("cancelled"); workcaseMapper.updateWorkcase(workcase); + + // 2. 发送系统评分消息到聊天室 + try { + TbWorkcaseDTO workcaseData = workcaseMapper.selectWorkcaseById(workcaseProcess.getWorkcaseId()); + if (workcaseData != null && workcaseData.getRoomId() != null) { + // 创建系统评分消息 + org.xyzh.api.workcase.dto.TbChatRoomMessageDTO commentMessage = new org.xyzh.api.workcase.dto.TbChatRoomMessageDTO(); + commentMessage.setMessageId(IdUtil.generateUUID()); + commentMessage.setOptsn(IdUtil.getOptsn()); + commentMessage.setRoomId(workcaseData.getRoomId()); + commentMessage.setSenderId("system"); + commentMessage.setSenderType("system"); // 系统消息 + commentMessage.setSenderName("系统"); + commentMessage.setMessageType("comment"); // 评分消息 + commentMessage.setContent("工单已撤销,请为本次服务评分"); + commentMessage.setStatus("sent"); + commentMessage.setCreator("system"); + + // 发送消息到聊天室 + chatRoomService.sendMessage(commentMessage); + logger.info("工单撤销,已发送系统评分消息: workcaseId={}, roomId={}", + workcaseProcess.getWorkcaseId(), workcaseData.getRoomId()); + } + } catch (Exception e) { + logger.error("发送系统评分消息失败: workcaseId={}", workcaseProcess.getWorkcaseId(), e); + // 不影响工单撤销流程,只记录错误日志 + } } workcaseProcess.setCreator(LoginUtil.getCurrentUserId()); diff --git a/urbanLifelineWeb/packages/workcase/src/api/workcase/workcase.ts b/urbanLifelineWeb/packages/workcase/src/api/workcase/workcase.ts index af9c8453..67f0a9ca 100644 --- a/urbanLifelineWeb/packages/workcase/src/api/workcase/workcase.ts +++ b/urbanLifelineWeb/packages/workcase/src/api/workcase/workcase.ts @@ -41,6 +41,15 @@ export const workcaseAPI = { return response.data }, + /** + * 撤销工单 + * @param workcaseId 工单ID + */ + async revokeWorkcase(workcaseId: string): Promise> { + const response = await api.post(`${this.baseUrl}/revoke/${workcaseId}`) + return response.data + }, + /** * 获取工单详情 * @param workcaseId 工单ID diff --git a/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue b/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue index d3640517..285b5184 100644 --- a/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue +++ b/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue @@ -1,11 +1,11 @@