| | |
| | | (3061, '入园承诺修改', 3002, 1, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:protocol:edit', '#', 'admin', NOW(), '访客后台按钮权限'), |
| | | (3062, '版本更新新增', 3003, 1, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:version:add', '#', 'admin', NOW(), '访客后台按钮权限'), |
| | | (3063, '版本更新删除', 3003, 2, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:version:remove', '#', 'admin', NOW(), '访客后台按钮权限'), |
| | | (3098, '版本更新编辑', 3003, 3, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:version:edit', '#', 'admin', NOW(), '访客后台按钮权限'), |
| | | (3064, '访客机新增', 3011, 1, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:machine:add', '#', 'admin', NOW(), '访客后台按钮权限'), |
| | | (3065, '访客机编辑', 3011, 2, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:machine:edit', '#', 'admin', NOW(), '访客后台按钮权限'), |
| | | (3066, '访客机启用禁用', 3011, 3, '#', NULL, NULL, '1', '1', 'F', '0', '0', 'visitor:machine:status', '#', 'admin', NOW(), '访客后台按钮权限'), |
| New file |
| | |
| | | -- 保存事件中心返回的访客登记记录ID,用于签离时更新中台状态 |
| | | ALTER TABLE `t_visitor_record` |
| | | ADD COLUMN `middle_platform_id` varchar(64) DEFAULT NULL COMMENT '事件中心访客登记记录ID' AFTER `register_no`; |
| | | |
| | | ALTER TABLE `t_visitor_record` |
| | | ADD KEY `idx_visitor_record_middle_platform_id` (`middle_platform_id`); |
| New file |
| | |
| | | -- 访客机版本更新增加编辑权限。 |
| | | -- 可重复执行:固定菜单 ID 使用 3098,并让已有“版本更新新增”权限的角色同步获得编辑权限。 |
| | | INSERT INTO `sys_menu` ( |
| | | `menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, |
| | | `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, |
| | | `create_by`, `create_time`, `remark` |
| | | ) VALUES ( |
| | | 3098, CONVERT(0xE78988E69CACE69BB4E696B0E7BC96E8BE91 USING utf8mb4), 3003, 3, '#', NULL, NULL, |
| | | '1', '1', 'F', '0', '0', 'visitor:version:edit', '#', |
| | | 'admin', NOW(), CONVERT(0xE8AEBFE5AEA2E5908EE58FB0E68C89E992AEE69D83E99990 USING utf8mb4) |
| | | ) |
| | | ON DUPLICATE KEY UPDATE |
| | | `menu_name` = VALUES(`menu_name`), |
| | | `parent_id` = VALUES(`parent_id`), |
| | | `order_num` = VALUES(`order_num`), |
| | | `perms` = VALUES(`perms`), |
| | | `status` = VALUES(`status`), |
| | | `update_by` = 'admin', |
| | | `update_time` = NOW(); |
| | | |
| | | INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) |
| | | SELECT source_role.`role_id`, 3098 |
| | | FROM `sys_role_menu` source_role |
| | | WHERE source_role.`menu_id` = 3062 |
| | | AND NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM `sys_role_menu` target_role |
| | | WHERE target_role.`role_id` = source_role.`role_id` |
| | | AND target_role.`menu_id` = 3098 |
| | | ); |
| | |
| | | import com.ruoyi.system.dto.SysVersionUpdateAddRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateDeleteRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateDetailRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateEditRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdatePageRequest; |
| | | import com.ruoyi.system.service.ISysVersionUpdateService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | return toAjax(versionUpdateService.add(request, getUsername())); |
| | | } |
| | | |
| | | /** |
| | | * 编辑版本更新 |
| | | */ |
| | | @Log(title = "版本更新", businessType = BusinessType.UPDATE) |
| | | @PreAuthorize("@ss.hasPermi('visitor:version:edit')") |
| | | @PostMapping("/edit") |
| | | public AjaxResult edit(@RequestBody SysVersionUpdateEditRequest request) |
| | | { |
| | | return toAjax(versionUpdateService.edit(request, getUsername())); |
| | | } |
| | | |
| | | @Log(title = "版本更新", businessType = BusinessType.INSERT) |
| | | @PreAuthorize("@ss.hasPermi('visitor:version:add')") |
| | | @PreAuthorize("@ss.hasAnyPermi('visitor:version:add,visitor:version:edit')") |
| | | @PostMapping("/upload") |
| | | public AjaxResult upload(MultipartFile file) |
| | | { |
| | |
| | | { |
| | | return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); |
| | | } |
| | | String password = user.getPassword(); |
| | | if (StringUtils.isEmpty(password)) |
| | | { |
| | | return R.fail("新增用户'" + user.getUserName() + "'失败,用户密码不能为空"); |
| | | } |
| | | user.setCreateBy(getUsername()); |
| | | user.setPassword(SecurityUtils.encryptPassword("123456")); |
| | | user.setPassword(SecurityUtils.encryptPassword(password)); |
| | | user.setRoleType(Integer.parseInt(user.getRoleId().toString())); |
| | | userService.insertUser(user); |
| | | return R.ok(); |
| | |
| | | */ |
| | | public final class BaseDataApiUtil { |
| | | |
| | | public static final String DEFAULT_BASE_URL = "http://58.20.112.148:18888"; |
| | | public static final String DEFAULT_BASE_URL = "http://192.168.1.146:18888"; |
| | | private static final String BASE_URL_PROPERTY = "base.data.api.url"; |
| | | private static final String BASE_URL_ENV = "BASE_DATA_API_URL"; |
| | | private static final String AUTHORIZATION_HEADER = "Authorization"; |
| | |
| | | * |
| | | * <p>目的:前端页面已绑定本系统字段名,直接透传中台字段会导致表格空列。</p> |
| | | * <p>影响范围:部门、区域、员工、访客机、门禁设备的查询和详情接口。</p> |
| | | * <p>注意:访客记录、入园承诺、版本更新暂无明确基础信息中台接口映射,仍由只读拦截保证不能写。</p> |
| | | * <p>注意:访客记录暂无明确中台接口映射,继续保持只读;入园承诺和版本更新属于本地维护模块。</p> |
| | | */ |
| | | public final class MiddlePlatformDataQuery { |
| | | |
| | |
| | | # 验证码类型 math 数字计算 char 字符验证 |
| | | captchaType: math |
| | | # 部署模式:standalone单机版 cluster集群版 |
| | | deploymentMode: standalone |
| | | deploymentMode: cluster |
| | | # 集群版是否对接中台:true对接 false不对接 |
| | | middlePlatformEnabled: false |
| | | # 开发环境配置 |
| | | server: |
| | | # 服务器的HTTP端口,默认为8080 |
| | | port: 8081 |
| | | port: 9098 |
| | | servlet: |
| | | # 应用的访问路径 |
| | | context-path: /laboratory/ |
| | | context-path: / |
| | | tomcat: |
| | | # tomcat的URI编码 |
| | | uri-encoding: UTF-8 |
| | |
| | | # redis 配置 |
| | | redis: |
| | | # 地址 |
| | | # host: 127.0.0.1 |
| | | # # 端口,默认为6379 |
| | | # port: 6379 |
| | | # # 数据库索引 |
| | | # database: 0 |
| | | # # 密码 |
| | | # password: 123456 |
| | | host: 127.0.0.1 |
| | | # 端口,默认为6379 |
| | | port: 6379 |
| | | port: 26379 |
| | | # 数据库索引 |
| | | database: 0 |
| | | # 密码 |
| | | password: 123456 |
| | | password: yuyue520 |
| | | # 连接超时时间 |
| | | timeout: 10s |
| | | lettuce: |
| | |
| | | druid: |
| | | # 主库数据源 |
| | | master: |
| | | url: jdbc:mysql://127.0.0.1:10633/laboratory?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true |
| | | url: jdbc:mysql://127.0.0.1:23306/fangkeji?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai |
| | | username: root |
| | | password: Laboratory@2025! |
| | | password: yuyue520 |
| | | # 从库数据源 |
| | | slave: |
| | | # 从数据源开关/默认关闭 |
| | |
| | | location: /file/ |
| | | qrLocation: /file/qrCode/ |
| | | accessPath: /file/ |
| | | allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF|.xls|.xlsx|.rar|.zip|.rar|.ppt|.PPT|.pptx|.CAJ|.caj|.bmp |
| | | allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF|.xls|.xlsx|.rar|.zip|.rar |
| | | wx: |
| | | conf: |
| | | appId: |
| | |
| | | # 项目相关配置 |
| | | spring: |
| | | profiles: |
| | | active: test |
| | | active: prod |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <configuration> |
| | | <!-- 日志存放路径 --> |
| | | <property name="log.path" value="${LOG_PATH:-/home/ruoyi/logs}" /> |
| | | <!-- 日志输出格式 --> |
| | | <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> |
| | | |
| | |
| | | </encoder> |
| | | </appender> |
| | | |
| | | <!-- 系统日志输出 --> |
| | | <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/sys-info.log</file> |
| | | <!-- 循环政策:基于时间创建日志文件 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志文件名格式 --> |
| | | <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> |
| | | <!-- 日志最大的历史 60天 --> |
| | | <maxHistory>60</maxHistory> |
| | | </rollingPolicy> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!-- 过滤的级别 --> |
| | | <level>INFO</level> |
| | | <!-- 匹配时的操作:接收(记录) --> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!-- 不匹配时的操作:拒绝(不记录) --> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/sys-error.log</file> |
| | | <!-- 循环政策:基于时间创建日志文件 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志文件名格式 --> |
| | | <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> |
| | | <!-- 日志最大的历史 60天 --> |
| | | <maxHistory>60</maxHistory> |
| | | </rollingPolicy> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!-- 过滤的级别 --> |
| | | <level>ERROR</level> |
| | | <!-- 匹配时的操作:接收(记录) --> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!-- 不匹配时的操作:拒绝(不记录) --> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <!-- 用户访问日志输出 --> |
| | | <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/sys-user.log</file> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 按天回滚 daily --> |
| | | <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> |
| | | <!-- 日志最大的历史 60天 --> |
| | | <maxHistory>60</maxHistory> |
| | | </rollingPolicy> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | </appender> |
| | | |
| | | <!-- 系统模块日志级别控制 --> |
| | | <logger name="com.ruoyi" level="info" /> |
| | | <!-- Spring日志级别控制 --> |
| | |
| | | <root level="info"> |
| | | <appender-ref ref="console" /> |
| | | </root> |
| | | |
| | | <!--系统操作日志--> |
| | | <root level="info"> |
| | | <appender-ref ref="file_info" /> |
| | | <appender-ref ref="file_error" /> |
| | | </root> |
| | | |
| | | <!--系统用户操作日志--> |
| | | <logger name="sys-user" level="info"> |
| | | <appender-ref ref="sys-user"/> |
| | | </logger> |
| | | </configuration> |
| | | </configuration> |
| | |
| | | |
| | | if (syncResult.isSuccess()) { |
| | | logger.info("访客登记数据已同步到对端,远程ID:{}", syncResult.get("data")); |
| | | // 可将远程ID保存到本地记录 |
| | | // record.setRemoteId(syncResult.getRemoteId()); |
| | | Object middlePlatformId = syncResult.get("data"); |
| | | if (middlePlatformId != null && StringUtils.isNotBlank(String.valueOf(middlePlatformId))) { |
| | | record.setMiddlePlatformId(String.valueOf(middlePlatformId)); |
| | | } |
| | | UpdateWrapper<SysVisitorRecord> wrapper = new UpdateWrapper<>(); |
| | | wrapper.eq("record_id", record.getRecordId()); // 指定ID条件 |
| | | visitorRecordMapper.update(record, wrapper); |
| | |
| | | visitorRecord.setSignVerifyMethod(request.getSignVerifyMethod()); |
| | | visitorRecord.setStatus(record.getStatus()); |
| | | visitorRecord.setRecordId(record.getRecordId()); |
| | | visitorRecord.setSignTime(new Date()); |
| | | Date signOutTime = new Date(); |
| | | visitorRecord.setSignTime(signOutTime); |
| | | record.setSignTime(signOutTime); |
| | | |
| | | UpdateWrapper<SysVisitorRecord> wrapperUpdate = new UpdateWrapper<>(); |
| | | wrapperUpdate.eq("record_id", visitorRecord.getRecordId()); // 指定ID条件 |
| | | sysVisitorRecordService.revokeAccess(record); |
| | | visitorRecordMapper.update(visitorRecord, wrapperUpdate); |
| | | sysVisitorRecordService.syncMiddlePlatformStatus(record); |
| | | |
| | | return success(); |
| | | } |
| | |
| | | return false; |
| | | } |
| | | |
| | | record.setSignTime(signoutTemp.getSignTime()); |
| | | record.setSignReceiverName(signoutTemp.getSignReceiverName()); |
| | | record.setSignVerifyMethod(signoutTemp.getSignVerifyMethod()); |
| | | record.setSignImageUrl(signoutTemp.getSignImageUrl()); |
| | | record.setStatus(status); |
| | | visitorRecordService.syncMiddlePlatformStatus(record); |
| | | |
| | | // 4. 更新签离临时表关联状态 |
| | | signoutTemp.setRelateStatus("1"); |
| | | signoutTemp.setRelateRecordId(record.getRecordId()); |
| | |
| | | @Autowired |
| | | private DahuaVisitorAccessService dahuaVisitorAccessService; |
| | | |
| | | @Autowired |
| | | private VisitorRegisterSyncService visitorRegisterSyncService; |
| | | |
| | | private static final String REDIS_KEY_PREFIX = "visitor:expire:"; |
| | | |
| | | /** |
| | |
| | | record.setRemark("逾期滞留,等待确认"); |
| | | record.setUpdateTime(now); |
| | | this.updateById(record); |
| | | syncMiddlePlatformStatus(record); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public void revokeAccess(SysVisitorRecord record) { |
| | | dahuaVisitorAccessService.revoke(record); |
| | | } |
| | | |
| | | /** |
| | | * 同步访客状态到事件中心;失败不回滚本地签离结果。 |
| | | */ |
| | | public void syncMiddlePlatformStatus(SysVisitorRecord record) { |
| | | visitorRegisterSyncService.syncVisitorStatus(record); |
| | | } |
| | | |
| | | private void registerGrantRollbackCompensation(SysVisitorRecord record, |
| | |
| | | redisTemplate.delete(REDIS_KEY_PREFIX + record.getRecordId()); |
| | | } |
| | | |
| | | syncMiddlePlatformStatus(record); |
| | | |
| | | return true; |
| | | } |
| | | |
| | |
| | | record.setRemark("系统自动签离(启动时检查)"); |
| | | record.setUpdateTime(now); |
| | | this.updateById(record); |
| | | syncMiddlePlatformStatus(record); |
| | | |
| | | // 删除 Redis Key |
| | | redisTemplate.delete(REDIS_KEY_PREFIX + record.getRecordId()); |
| | |
| | | } |
| | | |
| | | // 更新记录状态为超期自动签离 |
| | | Date signOutTime = new Date(); |
| | | LambdaUpdateWrapper<SysVisitorRecord> updateWrapper = new LambdaUpdateWrapper<>(); |
| | | updateWrapper.eq(SysVisitorRecord::getRecordId, record.getRecordId()) |
| | | .set(SysVisitorRecord::getStatus, "OVERDUE_AUTO_SIGNED_OUT") |
| | | .set(SysVisitorRecord::getSignTime, new Date()) // 签离时间设为有效期结束时间 |
| | | .set(SysVisitorRecord::getSignTime, signOutTime) // 签离时间设为当前时间 |
| | | .set(SysVisitorRecord::getSignReceiverName, "系统自动") |
| | | .set(SysVisitorRecord::getSignVerifyMethod, "AUTO") |
| | | .set(SysVisitorRecord::getSignImageUrl, null) |
| | |
| | | int updateCount = visitorRecordMapper.update(null, updateWrapper); |
| | | |
| | | if (updateCount > 0) { |
| | | record.setStatus("OVERDUE_AUTO_SIGNED_OUT"); |
| | | record.setSignTime(signOutTime); |
| | | visitorRecordService.syncMiddlePlatformStatus(record); |
| | | successCount++; |
| | | log.info("超期自动签离成功,recordId: {}, 姓名: {}, 身份证号: {}, 有效期结束时间: {}", |
| | | record.getRecordId(), |
| | |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | |
| | | @Value("${visitor.sync.url:}") |
| | | private String syncUrl; |
| | | |
| | | @Value("${visitor.sync.enabled:}") |
| | | @Value("${visitor.sync.enabled:false}") |
| | | private Boolean syncEnabled; |
| | | |
| | | /** 中台访客状态更新地址;未配置时由登记地址自动推导。 */ |
| | | @Value("${visitor.sync.status-url:}") |
| | | private String statusUrl; |
| | | |
| | | private final ObjectMapper objectMapper = new ObjectMapper(); |
| | | |
| | |
| | | */ |
| | | public AjaxResult syncVisitorRegister(SysVisitorRecord record) { |
| | | // 检查同步开关 |
| | | if (!syncEnabled) { |
| | | if (!Boolean.TRUE.equals(syncEnabled)) { |
| | | log.info("访客登记同步已关闭,跳过同步"); |
| | | return AjaxResult.success("同步功能未开启"); |
| | | } |
| | |
| | | boolean success = jsonNode.has("success") && jsonNode.get("success").asBoolean(); |
| | | |
| | | if (success) { |
| | | String remoteId = jsonNode.has("data") && jsonNode.get("data").has("id") |
| | | ? jsonNode.get("data").get("id").asText() : null; |
| | | JsonNode dataNode = jsonNode.get("data"); |
| | | String remoteId = null; |
| | | if (dataNode != null) { |
| | | if (dataNode.isObject() && dataNode.has("id")) { |
| | | remoteId = dataNode.get("id").asText(); |
| | | } else if (dataNode.isValueNode()) { |
| | | remoteId = dataNode.asText(); |
| | | } |
| | | } |
| | | log.info("访客登记同步成功,远程ID:{}", remoteId); |
| | | return AjaxResult.success(remoteId); |
| | | } else { |
| | |
| | | } catch (Exception e) { |
| | | log.error("访客登记同步异常", e); |
| | | return AjaxResult.error("同步异常:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将本地签离状态同步到事件中心。 |
| | | * |
| | | * <p>状态同步采用尽力而为策略:中台不可用时保留本地签离结果,避免访客机因网络问题无法完成签离。</p> |
| | | */ |
| | | public AjaxResult syncVisitorStatus(SysVisitorRecord record) { |
| | | if (!Boolean.TRUE.equals(syncEnabled)) { |
| | | return AjaxResult.success("访客状态同步已关闭"); |
| | | } |
| | | if (record == null || StringUtils.isBlank(record.getMiddlePlatformId())) { |
| | | log.warn("访客状态同步跳过:缺少中台访客登记记录ID,本地recordId={}", |
| | | record == null ? null : record.getRecordId()); |
| | | return AjaxResult.warn("缺少中台访客登记记录ID"); |
| | | } |
| | | |
| | | String middleStatus = toMiddleStatus(record.getStatus()); |
| | | if (middleStatus == null) { |
| | | log.info("访客状态 {} 无需同步到中台,middlePlatformId={}", |
| | | record.getStatus(), record.getMiddlePlatformId()); |
| | | return AjaxResult.success("当前状态无需同步"); |
| | | } |
| | | |
| | | String targetUrl = resolveStatusUrl(); |
| | | if (StringUtils.isBlank(targetUrl)) { |
| | | log.warn("访客状态同步跳过:未配置中台状态更新地址"); |
| | | return AjaxResult.warn("中台状态更新地址未配置"); |
| | | } |
| | | |
| | | try { |
| | | Map<String, Object> requestBody = new LinkedHashMap<>(); |
| | | requestBody.put("id", record.getMiddlePlatformId()); |
| | | requestBody.put("status", middleStatus); |
| | | if (record.getSignTime() != null) { |
| | | requestBody.put("signOutTime", formatTime(record.getSignTime())); |
| | | } |
| | | |
| | | String jsonBody = objectMapper.writeValueAsString(requestBody); |
| | | log.info("同步访客状态到中台:middlePlatformId={}, status={}, signOutTime={}", |
| | | record.getMiddlePlatformId(), middleStatus, requestBody.get("signOutTime")); |
| | | |
| | | HttpResponse response = HttpRequest.post(targetUrl) |
| | | .header("Content-Type", ContentType.JSON.getValue()) |
| | | .body(jsonBody) |
| | | .timeout(30000) |
| | | .execute(); |
| | | String responseBody = response.body(); |
| | | JsonNode jsonNode = objectMapper.readTree(responseBody); |
| | | boolean success = jsonNode.has("success") && jsonNode.get("success").asBoolean(); |
| | | if (success) { |
| | | return AjaxResult.success("访客状态同步成功"); |
| | | } |
| | | |
| | | String errorMessage = jsonNode.has("description") |
| | | ? jsonNode.get("description").asText() : "中台访客状态更新失败"; |
| | | log.error("中台访客状态更新失败,middlePlatformId={}, status={}, message={}", |
| | | record.getMiddlePlatformId(), middleStatus, errorMessage); |
| | | return AjaxResult.error(errorMessage); |
| | | } catch (Exception e) { |
| | | log.error("中台访客状态同步异常,middlePlatformId={}, status={}", |
| | | record.getMiddlePlatformId(), middleStatus, e); |
| | | return AjaxResult.error("中台状态同步异常:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | |
| | | params.put("pic", record.getFaceImageUrl()); |
| | | |
| | | return params; |
| | | } |
| | | |
| | | /** 根据登记接口地址推导同一事件中心下的状态更新接口。 */ |
| | | private String resolveStatusUrl() { |
| | | if (StringUtils.isNotBlank(statusUrl)) { |
| | | return statusUrl.trim(); |
| | | } |
| | | if (StringUtils.isBlank(syncUrl)) { |
| | | return null; |
| | | } |
| | | String registerPath = "/visitor-register"; |
| | | if (syncUrl.contains(registerPath)) { |
| | | return syncUrl.replace(registerPath, "/visitor-status-update"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** 将本地状态转换为事件中心字典值。 */ |
| | | private String toMiddleStatus(String status) { |
| | | if ("SIGNED_OUT".equals(status)) { |
| | | return "6"; |
| | | } |
| | | if ("OVERDUE_AUTO_SIGNED_OUT".equals(status)) { |
| | | return "5"; |
| | | } |
| | | if ("PENDING_CONFIRM".equals(status)) { |
| | | return "7"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | |
| | | String registerNo = record.getRegisterNo(); |
| | | return String.format("vms:visitor:%s:%s", dateStr, registerNo); |
| | | } |
| | | } |
| | | } |
| | |
| | | addressEnabled: false |
| | | # 验证码类型 math 数字计算 char 字符验证 |
| | | captchaType: math |
| | | |
| | | # 开发环境配置 |
| | | server: |
| | | # 服务器的HTTP端口,默认为8080 |
| | | port: 8082 |
| | | port: 9099 |
| | | servlet: |
| | | # 应用的访问路径 |
| | | context-path: / |
| | |
| | | max: 800 |
| | | # Tomcat启动初始化的线程数,默认值10 |
| | | min-spare: 100 |
| | | |
| | | # 访客登记同步配置 |
| | | visitor: |
| | | sync: |
| | | enabled: true # 是否启用同步 |
| | | url: http://192.168.1.146:18888/event-center/security-protection/visitor-register |
| | | # 状态更新地址;留空时按 url 自动替换为 visitor-status-update |
| | | status-url: ${VISITOR_SYNC_STATUS_URL:} |
| | | |
| | | # 日志配置 |
| | | logging: |
| | |
| | | # redis 配置 |
| | | redis: |
| | | # 地址 |
| | | # host: 127.0.0.1 |
| | | # # 端口,默认为6379 |
| | | # port: 6379 |
| | | # # 数据库索引 |
| | | # database: 0 |
| | | # # 密码 |
| | | # password: 123456 |
| | | host: 127.0.0.1 |
| | | # 端口,默认为6379 |
| | | port: 16379 |
| | | port: 26379 |
| | | # 数据库索引 |
| | | database: 0 |
| | | # 密码 |
| | | password: 8f5z9g52gx4bg |
| | | password: yuyue520 |
| | | # 连接超时时间 |
| | | timeout: 10s |
| | | lettuce: |
| | |
| | | druid: |
| | | # 主库数据源 |
| | | master: |
| | | url: jdbc:mysql://172.27.0.13:3306/xizang?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai |
| | | username: xzgt |
| | | password: changyun!6f2gshj6h3j |
| | | url: jdbc:mysql://127.0.0.1:23306/fangkeji?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai |
| | | username: root |
| | | password: yuyue520 |
| | | # 从库数据源 |
| | | slave: |
| | | # 从数据源开关/默认关闭 |
| | |
| | | location: /file/ |
| | | qrLocation: /file/qrCode/ |
| | | accessPath: /file/ |
| | | allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF |
| | | url: |
| | | prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path} |
| | | allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF|.xls|.xlsx|.rar|.zip|.rar|.m4a |
| | | wx: |
| | | conf: |
| | | appId: wxe91f1af7638aa5dd |
| | | secretId: a787e1a462715604e0c9528b6d8960d1 |
| | | #OSS及短信配置 |
| | | code: |
| | | config: |
| | | templateCodeTest: "SMS_154950909" |
| | | signNameTest: "阿里云短信测试" |
| | | accessKeyId: LTAI5tAdba8HtT1C6UqtSxBt |
| | | accessKeySecret: 0SRb6XGkciQDPWn2rYqbJtq2qRMDY8 |
| | | signName: "四川金达通信工程" |
| | | templateCode: "SMS_293985284" |
| | | cos: |
| | | client: |
| | | accessKey: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x |
| | | secretKey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU |
| | | bucket: xzgttest-1305134071 |
| | | bucketAddr: ap-chengdu |
| | | rootSrc: https://xzgttest-1305134071.cos.ap-chengdu.myqcloud.com/ |
| | | location: xizang |
| | | sms: |
| | | enable: true |
| | | appId: 1400957506 |
| | | secretid: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x |
| | | secretkey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU |
| | | sign: 畅云出行 |
| | | appId: |
| | | secretId: |
| | |
| | | visitor: |
| | | sync: |
| | | enabled: true # 是否启用同步 |
| | | url: http://58.20.112.148:18888/event-center/security-protection/visitor-register |
| | | url: http://192.168.1.146:18888/event-center/security-protection/visitor-register |
| | | # 状态更新地址;留空时按 url 自动替换为 visitor-status-update |
| | | status-url: ${VISITOR_SYNC_STATUS_URL:} |
| | | |
| | | # 日志配置 |
| | | logging: |
| | |
| | | wx: |
| | | conf: |
| | | appId: |
| | | secretId: |
| | | secretId: |
| | |
| | | # 项目相关配置 |
| | | spring: |
| | | profiles: |
| | | active: test |
| | | active: prod |
| | | |
| | | # 大华门禁访客人脸授权配置。默认关闭;原生库请使用对应操作系统的官方 SDK 目录。 |
| | | dahua: |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <configuration> |
| | | <!-- 日志存放路径 --> |
| | | <property name="log.path" value="/home/ruoyi/logs" /> |
| | | <!-- 日志输出格式 --> |
| | | <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> |
| | | |
| | |
| | | </encoder> |
| | | </appender> |
| | | |
| | | <!-- 系统日志输出 --> |
| | | <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/sys-info.log</file> |
| | | <!-- 循环政策:基于时间创建日志文件 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志文件名格式 --> |
| | | <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> |
| | | <!-- 日志最大的历史 60天 --> |
| | | <maxHistory>60</maxHistory> |
| | | </rollingPolicy> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!-- 过滤的级别 --> |
| | | <level>INFO</level> |
| | | <!-- 匹配时的操作:接收(记录) --> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!-- 不匹配时的操作:拒绝(不记录) --> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/sys-error.log</file> |
| | | <!-- 循环政策:基于时间创建日志文件 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志文件名格式 --> |
| | | <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> |
| | | <!-- 日志最大的历史 60天 --> |
| | | <maxHistory>60</maxHistory> |
| | | </rollingPolicy> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | <filter class="ch.qos.logback.classic.filter.LevelFilter"> |
| | | <!-- 过滤的级别 --> |
| | | <level>ERROR</level> |
| | | <!-- 匹配时的操作:接收(记录) --> |
| | | <onMatch>ACCEPT</onMatch> |
| | | <!-- 不匹配时的操作:拒绝(不记录) --> |
| | | <onMismatch>DENY</onMismatch> |
| | | </filter> |
| | | </appender> |
| | | |
| | | <!-- 用户访问日志输出 --> |
| | | <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/sys-user.log</file> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 按天回滚 daily --> |
| | | <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> |
| | | <!-- 日志最大的历史 60天 --> |
| | | <maxHistory>60</maxHistory> |
| | | </rollingPolicy> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | </appender> |
| | | |
| | | <!-- 系统模块日志级别控制 --> |
| | | <logger name="com.ruoyi" level="info" /> |
| | | <!-- Spring日志级别控制 --> |
| | |
| | | <root level="info"> |
| | | <appender-ref ref="console" /> |
| | | </root> |
| | | |
| | | <!--系统操作日志--> |
| | | <root level="info"> |
| | | <appender-ref ref="file_info" /> |
| | | <appender-ref ref="file_error" /> |
| | | </root> |
| | | |
| | | <!--系统用户操作日志--> |
| | | <logger name="sys-user" level="info"> |
| | | <appender-ref ref="sys-user"/> |
| | | </logger> |
| | | </configuration> |
| | | </configuration> |
| | |
| | | "/system/role/manage" |
| | | ); |
| | | |
| | | /** 本地仍可维护的白名单模块:账号管理、系统配置、首页、操作日志。 */ |
| | | /** 本地仍可维护的白名单模块:账号管理、系统配置、入园承诺、访客机版本、首页、操作日志。 */ |
| | | private static final List<String> LOCAL_MODULE_PREFIXES = Arrays.asList( |
| | | "/system/platform/admin", |
| | | "/system/role/manage", |
| | | "/system/visitor/config", |
| | | "/system/protocol/config", |
| | | "/system/version/update", |
| | | "/system/dashboard", |
| | | "/system/operation/log" |
| | | ); |
| | |
| | | /** 登记编号 */ |
| | | private String registerNo; |
| | | |
| | | /** 中台访客登记记录ID,登记同步成功后保存 */ |
| | | private String middlePlatformId; |
| | | |
| | | /** 访客姓名 */ |
| | | private String visitorName; |
| | | |
| | |
| | | /** 登记编号 */ |
| | | private String registerNo; |
| | | |
| | | /** 中台访客登记记录ID,登记同步成功后保存 */ |
| | | private String middlePlatformId; |
| | | |
| | | /** 访客姓名 */ |
| | | private String visitorName; |
| | | |
| New file |
| | |
| | | package com.ruoyi.system.dto; |
| | | |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 版本更新编辑请求 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Data |
| | | public class SysVersionUpdateEditRequest |
| | | { |
| | | /** 更新主键 */ |
| | | @ApiModelProperty("更新主键") |
| | | private Long updateId; |
| | | |
| | | /** 版本号 */ |
| | | @ApiModelProperty("版本号") |
| | | private String versionNo; |
| | | |
| | | /** 更新内容 */ |
| | | @ApiModelProperty("更新内容") |
| | | private String updateContent; |
| | | |
| | | /** 安装包地址 */ |
| | | @ApiModelProperty("安装包地址") |
| | | private String packageUrl; |
| | | |
| | | /** 安装包名称 */ |
| | | @ApiModelProperty("安装包名称") |
| | | private String packageName; |
| | | |
| | | /** 是否强制更新 */ |
| | | @ApiModelProperty("是否强制更新") |
| | | private Boolean forceUpdate; |
| | | } |
| | |
| | | |
| | | public int insertVersionUpdate(SysVersionUpdate versionUpdate); |
| | | |
| | | public int updateVersionUpdate(SysVersionUpdate versionUpdate); |
| | | |
| | | public int deleteByIds(@Param("updateIds") List<Long> updateIds); |
| | | } |
| | |
| | | import com.ruoyi.system.dto.SysVersionUpdateAddRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateDeleteRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateDetailRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateEditRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdatePageRequest; |
| | | import com.ruoyi.system.vo.SysVersionUpdateVO; |
| | | |
| | |
| | | |
| | | public int add(SysVersionUpdateAddRequest request, String username); |
| | | |
| | | public int edit(SysVersionUpdateEditRequest request, String username); |
| | | |
| | | public int delete(SysVersionUpdateDeleteRequest request); |
| | | } |
| | |
| | | import com.ruoyi.system.dto.SysVersionUpdateAddRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateDeleteRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateDetailRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdateEditRequest; |
| | | import com.ruoyi.system.dto.SysVersionUpdatePageRequest; |
| | | import com.ruoyi.system.mapper.SysVersionUpdateMapper; |
| | | import com.ruoyi.system.service.ISysVersionUpdateService; |
| | |
| | | { |
| | | throw new ServiceException("版本更新内容不能为空"); |
| | | } |
| | | String versionNo = StringUtils.trim(request.getVersionNo()); |
| | | SysVersionUpdate versionUpdate = buildVersionUpdate(request.getVersionNo(), request.getUpdateContent(), |
| | | request.getPackageUrl(), request.getPackageName(), request.getForceUpdate()); |
| | | versionUpdate.setCreateBy(username); |
| | | return versionUpdateMapper.insertVersionUpdate(versionUpdate); |
| | | } |
| | | |
| | | @Override |
| | | public int edit(SysVersionUpdateEditRequest request, String username) |
| | | { |
| | | if (request == null || request.getUpdateId() == null || request.getUpdateId() <= 0) |
| | | { |
| | | throw new ServiceException("更新主键不能为空"); |
| | | } |
| | | if (versionUpdateMapper.selectById(request.getUpdateId()) == null) |
| | | { |
| | | throw new ServiceException("版本更新记录不存在"); |
| | | } |
| | | SysVersionUpdate versionUpdate = buildVersionUpdate(request.getVersionNo(), request.getUpdateContent(), |
| | | request.getPackageUrl(), request.getPackageName(), request.getForceUpdate()); |
| | | versionUpdate.setUpdateId(request.getUpdateId()); |
| | | versionUpdate.setUpdateBy(username); |
| | | return versionUpdateMapper.updateVersionUpdate(versionUpdate); |
| | | } |
| | | |
| | | private SysVersionUpdate buildVersionUpdate(String requestVersionNo, String requestUpdateContent, |
| | | String requestPackageUrl, String requestPackageName, |
| | | Boolean requestForceUpdate) |
| | | { |
| | | String versionNo = StringUtils.trim(requestVersionNo); |
| | | if (StringUtils.isEmpty(versionNo)) |
| | | { |
| | | throw new ServiceException("版本号不能为空"); |
| | |
| | | throw new ServiceException("版本号格式不正确,请输入数字或使用小数点分隔"); |
| | | } |
| | | versionNo = versionNumber; |
| | | String updateContent = StringUtils.trim(request.getUpdateContent()); |
| | | String packageUrl = StringUtils.trim(request.getPackageUrl()); |
| | | String updateContent = StringUtils.trim(requestUpdateContent); |
| | | String packageUrl = StringUtils.trim(requestPackageUrl); |
| | | if (StringUtils.isEmpty(packageUrl)) |
| | | { |
| | | throw new ServiceException("安装包不能为空"); |
| | | } |
| | | String packageName = StringUtils.trim(request.getPackageName()); |
| | | String packageName = StringUtils.trim(requestPackageName); |
| | | SysVersionUpdate versionUpdate = new SysVersionUpdate(); |
| | | versionUpdate.setVersionNo(versionNo); |
| | | versionUpdate.setUpdateContent(updateContent); |
| | | versionUpdate.setPackageUrl(packageUrl); |
| | | versionUpdate.setPackageName(StringUtils.isEmpty(packageName) ? packageUrl : packageName); |
| | | versionUpdate.setForceUpdate(request.getForceUpdate() == null ? Boolean.FALSE : request.getForceUpdate()); |
| | | versionUpdate.setCreateBy(username); |
| | | return versionUpdateMapper.insertVersionUpdate(versionUpdate); |
| | | versionUpdate.setForceUpdate(requestForceUpdate == null ? Boolean.FALSE : requestForceUpdate); |
| | | return versionUpdate; |
| | | } |
| | | |
| | | @Override |
| | |
| | | ) |
| | | </insert> |
| | | |
| | | <update id="updateVersionUpdate" parameterType="SysVersionUpdate"> |
| | | update t_version_update |
| | | set version_no = #{versionNo}, |
| | | update_content = #{updateContent}, |
| | | package_url = #{packageUrl}, |
| | | package_name = #{packageName}, |
| | | force_update = #{forceUpdate}, |
| | | update_by = #{updateBy}, |
| | | update_time = sysdate() |
| | | where update_id = #{updateId} |
| | | and del_flag = '0' |
| | | </update> |
| | | |
| | | <update id="deleteByIds"> |
| | | update t_version_update |
| | | set del_flag = '1', |
| | |
| | | keyProperty="recordId"> |
| | | insert into t_visitor_record |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="middlePlatformId != null">middle_platform_id,</if> |
| | | <if test="registerNo != null">register_no,</if> |
| | | <if test="visitorName != null">visitor_name,</if> |
| | | <if test="idCard != null">id_card,</if> |
| | |
| | | </trim> |
| | | values |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="middlePlatformId != null">#{middlePlatformId},</if> |
| | | <if test="registerNo != null">#{registerNo},</if> |
| | | <if test="visitorName != null">#{visitorName},</if> |
| | | <if test="idCard != null">#{idCard},</if> |
| | |
| | | # 若依管理系统/生产环境 |
| | | VUE_APP_BASE_API = '/prod-api' |
| | | |
| | | # 前端静态资源使用相对路径,根目录和子目录部署均可用 |
| | | VUE_APP_PUBLIC_PATH = './' |
| | | |
| | | VUE_APP_VISITOR_FRONTEND_MODE = 'cluster' |
| | | VUE_APP_VISITOR_MIDDLE_PLATFORM_ENABLED = 'true' |
| | | VUE_APP_VISITOR_MIDDLE_PLATFORM_ENABLED = 'false' |
| | | # VUE_APP_VISITOR_LOGIN_MIDDLE_PLATFORM_ENABLED 未配置时默认跟随 VUE_APP_VISITOR_MIDDLE_PLATFORM_ENABLED |
| | | VUE_APP_VISITOR_API_BASE_URL = '/prod-api' |
| | |
| | | return request({ url: '/system/version/update/add', method: 'post', data }) |
| | | } |
| | | |
| | | export function editVersion(data) { |
| | | return request({ url: '/system/version/update/edit', method: 'post', data }) |
| | | } |
| | | |
| | | export function uploadVersionPackage(data) { |
| | | return request({ |
| | | url: '/system/version/update/upload', |
| | |
| | | this.passwordOpen = false |
| | | setTimeout(() => { |
| | | this.$store.dispatch('LogOut').then(() => { |
| | | location.href = '/login' |
| | | this.$router.push('/login') |
| | | }) |
| | | }, 600) |
| | | }).finally(() => { |
| | |
| | | type: 'warning' |
| | | }).then(() => { |
| | | this.$store.dispatch('LogOut').then(() => { |
| | | location.href = '/login' |
| | | this.$router.push('/login') |
| | | }) |
| | | }).catch(() => {}) |
| | | } |
| | |
| | | // 集群版对接中台时仍允许本地维护的页面:账号管理、系统配置、首页、操作日志。 |
| | | // 集群版对接中台时仍允许本地维护的页面:账号管理、系统配置、入园承诺、访客机版本、首页、操作日志。 |
| | | const localModulePaths = [ |
| | | '/index', |
| | | '/visitor/dashboard', |
| | | '/visitor/account', |
| | | '/visitor/system/config', |
| | | '/visitor/system/protocol', |
| | | '/visitor/system/version', |
| | | '/visitor/record/operation-log' |
| | | ] |
| | | |
| | |
| | | } |
| | | |
| | | export default new Router({ |
| | | mode: 'history', // 去掉url中的# |
| | | // hash 模式无需服务器为每个前端路由配置 index.html 回退,适合静态目录部署。 |
| | | mode: 'hash', |
| | | scrollBehavior: () => ({ y: 0 }), |
| | | routes: constantRoutes |
| | | }) |
| | |
| | | MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { |
| | | isRelogin.show = false; |
| | | store.dispatch('LogOut').then(() => { |
| | | location.href = '/index'; |
| | | location.hash = '#/index'; |
| | | }) |
| | | }).catch(() => { |
| | | isRelogin.show = false; |
| | |
| | | name: 'VisitorPlatformAdmin', |
| | | // 目的:初始化管理员列表、查询条件、编辑表单和重置密码表单状态。 |
| | | data() { |
| | | const strongPasswordMessage = '密码至少12位,且包含大小写字母和特殊字符' |
| | | const validateStrongPassword = (rule, value, callback) => { |
| | | if (!value || (value.length >= 12 && /[A-Z]/.test(value) && /[a-z]/.test(value) && /[^A-Za-z0-9]/.test(value))) { |
| | | callback() |
| | | return |
| | | } |
| | | callback(new Error(strongPasswordMessage)) |
| | | } |
| | | return { |
| | | loading: false, |
| | | showSearch: true, |
| | |
| | | userName: [{ required: true, message: '请输入账号', trigger: 'blur' }], |
| | | phonenumber: [{ required: true, message: '请输入手机号', trigger: 'blur' }], |
| | | roleId: [{ required: true, message: '请选择角色', trigger: 'change' }], |
| | | password: [{ required: true, message: '请输入密码', trigger: 'blur' }] |
| | | password: [ |
| | | { required: true, message: '请输入密码', trigger: 'blur' }, |
| | | { validator: validateStrongPassword, trigger: 'blur' } |
| | | ] |
| | | }, |
| | | passwordRules: { |
| | | password: [{ required: true, message: '请输入新密码', trigger: 'blur' }] |
| | | password: [ |
| | | { required: true, message: '请输入新密码', trigger: 'blur' }, |
| | | { validator: validateStrongPassword, trigger: 'blur' } |
| | | ] |
| | | } |
| | | } |
| | | }, |
| | |
| | | <div class="table-actions"> |
| | | <button type="button" @click="handleView(row)">查看</button> |
| | | <button |
| | | v-if="!isMiddlePlatformReadonly && $auth.hasPermi('visitor:version:edit')" |
| | | type="button" |
| | | @click="handleEdit(row)" |
| | | >编辑</button> |
| | | <button |
| | | v-if="!row.latest && !isMiddlePlatformReadonly && $auth.hasPermi('visitor:version:remove')" |
| | | type="button" |
| | | @click="handleDelete(row)" |
| | |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <el-dialog title="新建" :visible.sync="open" width="482px" append-to-body custom-class="version-create-dialog"> |
| | | <el-dialog :title="form.updateId ? '编辑' : '新建'" :visible.sync="open" width="482px" append-to-body custom-class="version-create-dialog"> |
| | | <form class="version-form" @submit.prevent="submitForm"> |
| | | <label class="version-row required"> |
| | | <span>版本号:</span> |
| | |
| | | <input ref="packageFile" type="file" hidden @change="handlePackageChange"> |
| | | <span><strong>{{ packageFileName ? packageFileName : '点击上传' }}</strong><i v-if="!packageFileName"> / </i>{{ packageFileName ? '' : '拖拽到此区域' }}</span> |
| | | </button> |
| | | <p class="upload-tip">请上传安装包文件,大小须小于200M</p> |
| | | <p class="upload-tip">{{ form.updateId ? '不重新上传将保留原安装包,文件大小须小于200M' : '请上传安装包文件,大小须小于200M' }}</p> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { listVersion, getVersion, addVersion, delVersion, uploadVersionPackage } from '@/api/visitor-admin/version' |
| | | import { getPageRows, getPageTotal, getResponseData } from '@/utils/visitor-admin' |
| | | import { listVersion, getVersion, addVersion, editVersion, delVersion, uploadVersionPackage } from '@/api/visitor-admin/version' |
| | | import { getPageRows, getPageTotal, getResponseData, normalizeBoolean } from '@/utils/visitor-admin' |
| | | |
| | | const MAX_PACKAGE_SIZE = 200 * 1024 * 1024 |
| | | |
| | |
| | | const value = String(versionNo || '').replace(/^[vV]/, '') |
| | | return value ? `v${value}` : '-' |
| | | }, |
| | | // 目的:打开新增版本弹窗;中台只读模式下禁止本地新增。 |
| | | // 目的:打开新增版本弹窗;非本地维护页面的中台只读模式下禁止本地新增。 |
| | | handleAdd() { |
| | | if (this.isMiddlePlatformReadonly) { |
| | | this.denyMiddlePlatformWrite() |
| | | return |
| | | } |
| | | this.form = { forceUpdate: false, versionNo: '', updateContent: '', packageName: '', packageUrl: '' } |
| | | this.form = { updateId: null, forceUpdate: false, versionNo: '', updateContent: '', packageName: '', packageUrl: '' } |
| | | this.packageFile = null |
| | | this.packageFileName = '' |
| | | this.packageDragActive = false |
| | |
| | | this.$refs.packageFile.value = '' |
| | | } |
| | | this.open = true |
| | | }, |
| | | // 目的:读取完整版本信息后进入编辑态,未选择新文件时保留原安装包。 |
| | | handleEdit(row) { |
| | | if (this.isMiddlePlatformReadonly) { |
| | | this.denyMiddlePlatformWrite() |
| | | return |
| | | } |
| | | getVersion(row.updateId).then(res => { |
| | | const data = getResponseData(res) || {} |
| | | this.form = { |
| | | updateId: data.updateId, |
| | | versionNo: String(data.versionNo || '').replace(/^[vV]/, ''), |
| | | updateContent: data.updateContent || '', |
| | | packageUrl: data.packageUrl || '', |
| | | packageName: data.packageName || '', |
| | | forceUpdate: normalizeBoolean(data.forceUpdate) |
| | | } |
| | | this.packageFile = null |
| | | this.packageFileName = data.packageName || data.packageUrl || '' |
| | | this.packageDragActive = false |
| | | this.open = true |
| | | this.$nextTick(() => { |
| | | if (this.$refs.packageFile) { |
| | | this.$refs.packageFile.value = '' |
| | | } |
| | | }) |
| | | }) |
| | | }, |
| | | // 目的:版本号只保留数字和小数点,支持多段版本号输入。 |
| | | handleVersionNoInput(event) { |
| | |
| | | input.value = '' |
| | | } |
| | | this.packageFile = null |
| | | this.packageFileName = '' |
| | | this.form.packageName = '' |
| | | this.form.packageUrl = '' |
| | | this.packageFileName = this.form.updateId ? (this.form.packageName || this.form.packageUrl || '') : '' |
| | | return |
| | | } |
| | | this.packageFile = file || null |
| | | this.packageFileName = file ? file.name : '' |
| | | if (file) { |
| | | this.form.packageName = file.name |
| | | this.form.packageUrl = '' |
| | | } |
| | | }, |
| | | // 目的:校验并提交新增版本;中台只读模式下禁止提交。 |
| | | // 目的:统一校验并提交新增或编辑版本,编辑时仅在重新选择文件后上传安装包。 |
| | | submitForm() { |
| | | if (this.isMiddlePlatformReadonly) { |
| | | this.denyMiddlePlatformWrite() |
| | | return |
| | | } |
| | | if (!/^\d+(\.\d+)*$/.test(this.form.versionNo || '') || !this.packageFile) { |
| | | const isEdit = Boolean(this.form.updateId) |
| | | if (!/^\d+(\.\d+)*$/.test(this.form.versionNo || '') || (!this.packageFile && !this.form.packageUrl)) { |
| | | this.$modal.msgError('请填写版本号并上传安装包') |
| | | return |
| | | } |
| | | if (this.packageFile.size >= MAX_PACKAGE_SIZE) { |
| | | if (this.packageFile && this.packageFile.size >= MAX_PACKAGE_SIZE) { |
| | | this.$modal.msgError('操作失败:安装包大小必须小于200M') |
| | | return |
| | | } |
| | | const uploadRequest = this.packageFile ? this.uploadPackage() : Promise.resolve() |
| | | uploadRequest.then(() => isEdit ? editVersion(this.form) : addVersion(this.form)).then(() => { |
| | | this.$modal.msgSuccess(isEdit ? '编辑成功' : '新增成功') |
| | | this.open = false |
| | | this.getList() |
| | | }) |
| | | }, |
| | | // 目的:上传当前选择的安装包,并把服务端文件信息回填到版本表单。 |
| | | uploadPackage() { |
| | | const packageFile = this.packageFile |
| | | const formData = new FormData() |
| | | formData.append('file', this.packageFile) |
| | | uploadVersionPackage(formData).then(res => { |
| | | formData.append('file', packageFile) |
| | | return uploadVersionPackage(formData).then(res => { |
| | | const data = getResponseData(res) || {} |
| | | this.form.packageUrl = data.fileName || data.url || '' |
| | | this.form.packageName = data.originalFilename || data.newFileName || this.packageFile.name |
| | | this.form.packageName = data.originalFilename || data.newFileName || packageFile.name |
| | | if (!this.form.packageUrl) { |
| | | throw new Error('安装包上传失败,未返回文件地址') |
| | | } |
| | | return addVersion(this.form) |
| | | }).then(() => { |
| | | this.$modal.msgSuccess('新增成功') |
| | | this.open = false |
| | | this.getList() |
| | | }) |
| | | }, |
| | | // 目的:查看任意版本的完整更新信息。 |
| | |
| | | // 部署生产环境和开发环境下的URL。 |
| | | // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 |
| | | // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 |
| | | publicPath: process.env.NODE_ENV === "production" ? "/" : "/", |
| | | // 使用相对路径,根目录和子目录部署均可直接加载静态资源。 |
| | | publicPath: process.env.NODE_ENV === "production" |
| | | ? (process.env.VUE_APP_PUBLIC_PATH || "./") |
| | | : "/", |
| | | // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist) |
| | | outputDir: 'dist', |
| | | // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下) |