445 lines
12 KiB
Dart
445 lines
12 KiB
Dart
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>[];
|
||
}
|
||
}
|