import 'package:dio/dio.dart'; import '../models/schedule.dart'; import '../models/sys_config.dart'; import '../models/sys_log.dart'; import '../models/sys_menu.dart'; import '../models/sys_role.dart'; import '../models/sys_user.dart'; import 'api_client.dart'; /// 系统管理服务:用户、角色、菜单、参数配置、日志、定时任务、密码修改。 /// /// 对接真实「爱视后台」/api/sys/* 接口。 /// 响应已由 [ApiInterceptor] 解包 `{code,msg,data}` 信封,直接消费 data。 class SystemService { SystemService({Dio? dio}) : _dio = dio ?? ApiClient.instance.dio; final Dio _dio; // ================ 用户管理 ================ /// 获取用户列表。 /// /// GET /sys/user/list?page=&limit=&username= Future> getUserList({ int page = 1, int limit = 20, String? username, String? orgId, }) async { final response = await _dio.get( '/sys/user/list', queryParameters: { 'page': page, 'limit': limit, if (username != null) 'username': username, if (orgId != null) 'orgId': orgId, }, ); final list = _extractList(response.data); return list .whereType>() .map(SysUser.fromJson) .toList(); } /// 获取用户详情。 /// /// GET /sys/user/info/{id} Future getUserInfo(String id) async { final response = await _dio.get('/sys/user/info/$id'); final data = response.data; if (data is! Map) { throw ApiException(500, '用户信息加载失败:$id'); } return SysUser.fromJson(data); } /// 新增或修改用户。 /// /// 与 Web 端一致:携带 id 走 /sys/user/update(修改,密码留空表示不修改), /// 否则走 /sys/user/save(新增,密码必填)。 Future saveUser(Map data) async { final hasId = data['id'] != null && data['id'].toString().isNotEmpty; final url = hasId ? '/sys/user/update' : '/sys/user/save'; await _dio.post(url, data: data); } /// 批量删除用户。 /// /// POST /sys/user/delete,body 为 `{ids: [...]}`。 Future deleteUser(List ids) async { await _dio.post( '/sys/user/delete', data: {'ids': ids}, ); } // ================ 角色管理 ================ /// 获取角色列表(分页)。 /// /// GET /sys/role/list?page=&limit= Future> getRoleList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/role/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(SysRole.fromJson) .toList(); } /// 获取角色下拉选项(不分页,用于用户表单选择角色)。 /// /// GET /sys/role/select Future> getRoleSelect() async { final response = await _dio.get('/sys/role/select'); final list = _extractList(response.data); return list .whereType>() .map(SysRole.fromJson) .toList(); } /// 获取角色详情。 /// /// GET /sys/role/info/{id} Future getRoleInfo(String id) async { final response = await _dio.get('/sys/role/info/$id'); final data = response.data; if (data is! Map) { throw ApiException(500, '角色信息加载失败:$id'); } return SysRole.fromJson(data); } /// 新增或修改角色。 /// /// POST /sys/role/save,body 为角色数据(可含 menuIdList)。 Future saveRole(Map data) async { await _dio.post('/sys/role/save', data: data); } /// 批量删除角色。 /// /// POST /sys/role/delete,body 为 `{ids: [...]}`。 Future deleteRole(List ids) async { await _dio.post( '/sys/role/delete', data: {'ids': ids}, ); } // ================ 菜单管理 ================ /// 获取当前用户可见的导航菜单树。 /// /// GET /sys/menu/nav Future> getMenuNav() async { final response = await _dio.get('/sys/menu/nav'); final list = _extractList(response.data); return list .whereType>() .map(SysMenu.fromJson) .toList(); } /// 获取菜单列表(分页或全量,后端可能返回平铺列表)。 /// /// GET /sys/menu/list Future> getMenuList() async { final response = await _dio.get('/sys/menu/list'); final list = _extractList(response.data); return list .whereType>() .map(SysMenu.fromJson) .toList(); } /// 获取菜单下拉选项(用于角色权限树选择)。 /// /// GET /sys/menu/select Future> getMenuSelect() async { final response = await _dio.get('/sys/menu/select'); final list = _extractList(response.data); return list .whereType>() .map(SysMenu.fromJson) .toList(); } /// 获取菜单详情。 /// /// GET /sys/menu/info/{id} Future getMenuInfo(String id) async { final response = await _dio.get('/sys/menu/info/$id'); final data = response.data; if (data is! Map) { throw ApiException(500, '菜单信息加载失败:$id'); } return SysMenu.fromJson(data); } /// 新增或修改菜单。 /// /// POST /sys/menu/save,body 为菜单数据。 Future saveMenu(Map data) async { await _dio.post('/sys/menu/save', data: data); } /// 删除菜单(单个,含子菜单校验)。 /// /// POST /sys/menu/delete/{id} Future deleteMenu(String id) async { await _dio.post('/sys/menu/delete/$id'); } // ================ 参数配置 ================ /// 获取参数配置列表(分页)。 /// /// GET /sys/config/list?page=&limit= Future> getConfigList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/config/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(SysConfig.fromJson) .toList(); } /// 获取参数配置详情。 /// /// GET /sys/config/info/{id} Future getConfigInfo(String id) async { final response = await _dio.get('/sys/config/info/$id'); final data = response.data; if (data is! Map) { throw ApiException(500, '参数配置加载失败:$id'); } return SysConfig.fromJson(data); } /// 新增或修改参数配置。 /// /// POST /sys/config/save,body 为配置数据。 Future saveConfig(Map data) async { await _dio.post('/sys/config/save', data: data); } /// 批量删除参数配置。 /// /// POST /sys/config/delete,body 为 `{ids: [...]}`。 Future deleteConfig(List ids) async { await _dio.post( '/sys/config/delete', data: {'ids': ids}, ); } // ================ 日志管理 ================ /// 获取操作日志列表(分页)。 /// /// GET /sys/log/list?page=&limit= Future> getLogList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/log/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(SysLog.fromJson) .toList(); } /// 获取审计日志列表(分页)。 /// /// GET /sys/auditLog/list?page=&limit= Future> getAuditLogList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/auditLog/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(SysLog.fromJson) .toList(); } /// 获取 API 调用日志列表(分页)。 /// /// GET /sys/scheduleLog/list?page=&limit= /// 注意:接口路径为 scheduleLog,用于定时任务调用日志查询。 Future> getApiJstCallLogList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/scheduleLog/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(SysLog.fromJson) .toList(); } // ================ 定时任务 ================ /// 获取定时任务列表(分页)。 /// /// GET /sys/schedule/list?page=&limit= Future> getScheduleList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/schedule/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(Schedule.fromJson) .toList(); } /// 获取定时任务详情。 /// /// GET /sys/schedule/info/{id} Future getScheduleInfo(String id) async { final response = await _dio.get('/sys/schedule/info/$id'); final data = response.data; if (data is! Map) { throw ApiException(500, '定时任务信息加载失败:$id'); } return Schedule.fromJson(data); } /// 新增或修改定时任务。 /// /// POST /sys/schedule/save,body 为任务数据。 Future saveSchedule(Map data) async { await _dio.post('/sys/schedule/save', data: data); } /// 批量删除定时任务。 /// /// POST /sys/schedule/delete,body 为 `{ids: [...]}`。 Future deleteSchedule(List ids) async { await _dio.post( '/sys/schedule/delete', data: {'ids': ids}, ); } /// 暂停定时任务。 /// /// POST /sys/schedule/pause,body 为 `{jobId: ...}`。 Future pauseSchedule(String jobId) async { await _dio.post( '/sys/schedule/pause', data: {'jobId': jobId}, ); } /// 恢复定时任务。 /// /// POST /sys/schedule/resume,body 为 `{jobId: ...}`。 Future resumeSchedule(String jobId) async { await _dio.post( '/sys/schedule/resume', data: {'jobId': jobId}, ); } /// 立即执行一次定时任务。 /// /// POST /sys/schedule/run,body 为 `{jobId: ...}`。 Future runSchedule(String jobId) async { await _dio.post( '/sys/schedule/run', data: {'jobId': jobId}, ); } /// 获取定时任务日志列表(分页)。 /// /// GET /sys/scheduleLog/list?page=&limit= /// /// 与 [getApiJstCallLogList] 相同路径,保留独立方法以区分语义。 Future> getScheduleLogList({ int page = 1, int limit = 20, }) async { final response = await _dio.get( '/sys/scheduleLog/list', queryParameters: {'page': page, 'limit': limit}, ); final list = _extractList(response.data); return list .whereType>() .map(SysLog.fromJson) .toList(); } // ================ 密码修改 ================ /// 修改当前用户密码。 /// /// POST /sys/user/password,body 为 `{password, newPassword}`。 Future updatePassword( String oldPassword, String newPassword, ) async { await _dio.post( '/sys/user/password', data: {'password': oldPassword, 'newPassword': newPassword}, ); } // ================ 工具方法 ================ /// 兼容列表响应的三种形态: /// `{list: [...], totalCount}` / `{page: {list, totalCount}}` / 直接数组。 static List _extractList(dynamic data) { if (data is List) { return data; } if (data is Map) { final list = data['list']; if (list is List) { return list; } final page = data['page']; if (page is Map && page['list'] is List) { return page['list'] as List; } } return const []; } }