Files
CloudHome-Ops/lib/services/sys_service.dart

445 lines
12 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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