refactor: 青龙隔离从按用户改为按部门

- 删除 QlUserConfigEntity/Mapper/Service,新增 QlDeptConfigEntity/Mapper/Service
- QinglongService: userId → deptIds,支持多部门合并展示(mergeDataFromDepts)
- AdminQinglongController: 通过用户departmentId获取部门青龙配置
- 新增 AdminQlDeptConfigController: 部门青龙App绑定/解绑/未绑定部门列表
- BaseSysLoginServiceImpl: 移除登录时自动创建青龙App的逻辑
This commit is contained in:
OpenCode
2026-07-11 00:11:11 +08:00
parent 754b7f4782
commit 6233e119d4
11 changed files with 445 additions and 168 deletions
@@ -46,8 +46,6 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
private final BaseSysPermsService baseSysPermsService;
private final com.cool.modules.task.service.QinglongService qinglongService;
@org.springframework.beans.factory.annotation.Value("${cool.captcha.enable:true}")
private boolean captchaEnable;
@@ -106,7 +104,6 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
if (captchaEnable) {
coolCache.del("verify:img:" + baseSysLoginDto.getCaptchaId());
}
qinglongService.ensureUserApp(baseSysUserEntity.getId());
return generateToken(roleIds, baseSysUserEntity, null);
}
@@ -4,9 +4,14 @@ import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.cool.core.request.R;
import com.cool.core.util.CoolSecurityUtil;
import com.cool.modules.base.entity.sys.BaseSysUserEntity;
import com.cool.modules.base.mapper.sys.BaseSysUserMapper;
import com.cool.modules.task.service.QinglongService;
import com.mybatisflex.core.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@@ -17,15 +22,21 @@ import org.springframework.web.bind.annotation.*;
public class AdminQinglongController {
final private QinglongService qinglongService;
final private BaseSysUserMapper baseSysUserMapper;
private Long currentUserId() {
return CoolSecurityUtil.getCurrentUserId();
private List<Long> currentUserDeptIds() {
Long userId = CoolSecurityUtil.getCurrentUserId();
BaseSysUserEntity user = baseSysUserMapper.selectOneById(userId);
if (user != null && user.getDepartmentId() != null) {
return Collections.singletonList(user.getDepartmentId());
}
return Collections.emptyList();
}
@Operation(summary = "检查连接")
@GetMapping("/check")
public R check() {
return R.ok(qinglongService.checkConnection(currentUserId()));
return R.ok(qinglongService.checkConnection(currentUserDeptIds()));
}
@Operation(summary = "获取脚本列表")
@@ -33,43 +44,43 @@ public class AdminQinglongController {
public R scripts(@RequestParam(required = false) String searchValue,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
return R.ok(qinglongService.getScripts(currentUserId(), searchValue, page, size));
return R.ok(qinglongService.getScripts(currentUserDeptIds(), searchValue, page, size));
}
@Operation(summary = "获取脚本详情")
@GetMapping("/scripts/{fileName}")
public R scriptDetail(@PathVariable String fileName) {
return R.ok(qinglongService.getScript(currentUserId(), fileName));
return R.ok(qinglongService.getScript(currentUserDeptIds(), fileName));
}
@Operation(summary = "添加脚本")
@PostMapping("/scripts")
public R addScript(@RequestBody JSONObject body) {
return R.ok(qinglongService.addScript(currentUserId(), body));
return R.ok(qinglongService.addScript(currentUserDeptIds(), body));
}
@Operation(summary = "更新脚本")
@PutMapping("/scripts/{fileName}")
public R updateScript(@PathVariable String fileName, @RequestBody JSONObject body) {
return R.ok(qinglongService.updateScript(currentUserId(), fileName, body));
return R.ok(qinglongService.updateScript(currentUserDeptIds(), fileName, body));
}
@Operation(summary = "删除脚本")
@DeleteMapping("/scripts")
public R deleteScript(@RequestBody JSONArray fileNames) {
return R.ok(qinglongService.deleteScript(currentUserId(), fileNames.toList(String.class)));
return R.ok(qinglongService.deleteScript(currentUserDeptIds(), fileNames.toList(String.class)));
}
@Operation(summary = "运行脚本")
@PostMapping("/scripts/run")
public R runScript(@RequestBody JSONObject body) {
return R.ok(qinglongService.runScript(currentUserId(), body));
return R.ok(qinglongService.runScript(currentUserDeptIds(), body));
}
@Operation(summary = "停止脚本")
@PostMapping("/scripts/stop")
public R stopScript(@RequestBody JSONObject body) {
return R.ok(qinglongService.stopScript(currentUserId(), body));
return R.ok(qinglongService.stopScript(currentUserDeptIds(), body));
}
@Operation(summary = "获取环境变量列表")
@@ -77,37 +88,37 @@ public class AdminQinglongController {
public R envs(@RequestParam(required = false) String searchValue,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
return R.ok(qinglongService.getEnvs(currentUserId(), searchValue, page, size));
return R.ok(qinglongService.getEnvs(currentUserDeptIds(), searchValue, page, size));
}
@Operation(summary = "添加环境变量")
@PostMapping("/envs")
public R addEnv(@RequestBody JSONArray envs) {
return R.ok(qinglongService.addEnv(currentUserId(), envs));
return R.ok(qinglongService.addEnv(currentUserDeptIds(), envs));
}
@Operation(summary = "更新环境变量")
@PutMapping("/envs")
public R updateEnv(@RequestBody JSONObject body) {
return R.ok(qinglongService.updateEnv(currentUserId(), body));
return R.ok(qinglongService.updateEnv(currentUserDeptIds(), body));
}
@Operation(summary = "删除环境变量")
@DeleteMapping("/envs")
public R deleteEnv(@RequestBody JSONArray ids) {
return R.ok(qinglongService.deleteEnv(currentUserId(), ids));
return R.ok(qinglongService.deleteEnv(currentUserDeptIds(), ids));
}
@Operation(summary = "启用环境变量")
@PutMapping("/envs/enable")
public R enableEnv(@RequestBody JSONArray ids) {
return R.ok(qinglongService.enableEnv(currentUserId(), ids));
return R.ok(qinglongService.enableEnv(currentUserDeptIds(), ids));
}
@Operation(summary = "禁用环境变量")
@PutMapping("/envs/disable")
public R disableEnv(@RequestBody JSONArray ids) {
return R.ok(qinglongService.disableEnv(currentUserId(), ids));
return R.ok(qinglongService.disableEnv(currentUserDeptIds(), ids));
}
@Operation(summary = "获取青龙定时任务列表")
@@ -115,54 +126,54 @@ public class AdminQinglongController {
public R crons(@RequestParam(required = false) String searchValue,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
return R.ok(qinglongService.getCrons(currentUserId(), searchValue, page, size));
return R.ok(qinglongService.getCrons(currentUserDeptIds(), searchValue, page, size));
}
@Operation(summary = "添加青龙定时任务")
@PostMapping("/crons")
public R addCron(@RequestBody JSONObject body) {
return R.ok(qinglongService.addCron(currentUserId(), body));
return R.ok(qinglongService.addCron(currentUserDeptIds(), body));
}
@Operation(summary = "更新青龙定时任务")
@PutMapping("/crons")
public R updateCron(@RequestBody JSONObject body) {
return R.ok(qinglongService.updateCron(currentUserId(), body));
return R.ok(qinglongService.updateCron(currentUserDeptIds(), body));
}
@Operation(summary = "删除青龙定时任务")
@DeleteMapping("/crons")
public R deleteCron(@RequestBody JSONArray ids) {
return R.ok(qinglongService.deleteCron(currentUserId(), ids));
return R.ok(qinglongService.deleteCron(currentUserDeptIds(), ids));
}
@Operation(summary = "运行青龙定时任务")
@PutMapping("/crons/run")
public R runCron(@RequestBody JSONArray ids) {
return R.ok(qinglongService.runCron(currentUserId(), ids));
return R.ok(qinglongService.runCron(currentUserDeptIds(), ids));
}
@Operation(summary = "停止青龙定时任务")
@PutMapping("/crons/stop")
public R stopCron(@RequestBody JSONArray ids) {
return R.ok(qinglongService.stopCron(currentUserId(), ids));
return R.ok(qinglongService.stopCron(currentUserDeptIds(), ids));
}
@Operation(summary = "启用青龙定时任务")
@PutMapping("/crons/enable")
public R enableCron(@RequestBody JSONArray ids) {
return R.ok(qinglongService.enableCron(currentUserId(), ids));
return R.ok(qinglongService.enableCron(currentUserDeptIds(), ids));
}
@Operation(summary = "禁用青龙定时任务")
@PutMapping("/crons/disable")
public R disableCron(@RequestBody JSONArray ids) {
return R.ok(qinglongService.disableCron(currentUserId(), ids));
return R.ok(qinglongService.disableCron(currentUserDeptIds(), ids));
}
@Operation(summary = "获取青龙任务日志")
@GetMapping("/crons/{cronId}/log")
public R cronLog(@PathVariable String cronId) {
return R.ok(qinglongService.getCronLog(currentUserId(), cronId));
return R.ok(qinglongService.getCronLog(currentUserDeptIds(), cronId));
}
}
@@ -0,0 +1,143 @@
package com.cool.modules.task.controller.admin;
import cn.hutool.json.JSONObject;
import com.cool.core.request.R;
import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity;
import com.cool.modules.base.service.sys.BaseSysDepartmentService;
import com.cool.modules.task.entity.QlDeptConfigEntity;
import com.cool.modules.task.service.QlDeptConfigService;
import com.cool.modules.task.service.QinglongService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@Tag(name = "青龙部门配置", description = "部门青龙App绑定管理")
@RestController
@RequestMapping("/admin/task/qlDeptConfig")
@RequiredArgsConstructor
public class AdminQlDeptConfigController {
final private QlDeptConfigService service;
final private QinglongService qinglongService;
final private BaseSysDepartmentService baseSysDepartmentService;
@Operation(summary = "分页查询")
@PostMapping("/page")
public R page(@RequestBody JSONObject body) {
int page = body.getInt("page", 1);
int size = body.getInt("size", 20);
String keyWord = body.getStr("keyWord");
com.mybatisflex.core.query.QueryWrapper qw = com.mybatisflex.core.query.QueryWrapper.create();
if (keyWord != null && !keyWord.isEmpty()) {
qw.like(QlDeptConfigEntity::getDeptName, keyWord);
}
com.mybatisflex.core.paginate.Page<QlDeptConfigEntity> result = service.page(
new com.mybatisflex.core.paginate.Page<>(page, size), qw);
return R.ok(result);
}
@Operation(summary = "列表查询")
@PostMapping("/list")
public R list(@RequestBody JSONObject body) {
return R.ok(service.list());
}
@Operation(summary = "详情")
@GetMapping("/info")
public R info(@RequestParam Long id) {
return R.ok(service.getById(id));
}
@Operation(summary = "新增")
@PostMapping("/add")
public R add(@RequestBody JSONObject body) {
QlDeptConfigEntity entity = new QlDeptConfigEntity();
entity.setDeptId(body.getLong("deptId"));
entity.setDeptName(body.getStr("deptName"));
entity.setClientId(body.getStr("clientId"));
entity.setClientSecret(body.getStr("clientSecret"));
service.save(entity);
return R.ok(entity);
}
@Operation(summary = "更新")
@PostMapping("/update")
public R update(@RequestBody JSONObject body) {
Long id = body.getLong("id");
QlDeptConfigEntity entity = service.getById(id);
if (entity == null) {
return R.error("记录不存在");
}
if (body.containsKey("accessToken")) {
entity.setAccessToken(body.getStr("accessToken"));
}
if (body.containsKey("tokenExpire")) {
entity.setTokenExpire(body.getLong("tokenExpire"));
}
service.updateById(entity);
return R.ok(entity);
}
@Operation(summary = "删除")
@PostMapping("/delete")
public R delete(@RequestBody JSONObject body) {
Object ids = body.get("ids");
if (ids instanceof List) {
service.removeByIds((List<Long>) ids);
}
return R.ok();
}
@Operation(summary = "为部门绑定青龙App")
@PostMapping("/bind")
public R bind(@RequestBody JSONObject body) {
Long deptId = body.getLong("deptId");
if (deptId == null) {
return R.error("部门ID不能为空");
}
BaseSysDepartmentEntity dept = baseSysDepartmentService.getById(deptId);
if (dept == null) {
return R.error("部门不存在");
}
QlDeptConfigEntity config = qinglongService.ensureDeptApp(deptId, dept.getName());
if (config == null) {
return R.error("创建青龙应用失败,请检查青龙面板连接");
}
return R.ok(config);
}
@Operation(summary = "解绑部门青龙App")
@PostMapping("/unbind")
public R unbind(@RequestBody JSONObject body) {
Long deptId = body.getLong("deptId");
if (deptId == null) {
return R.error("部门ID不能为空");
}
qinglongService.deleteDeptApp(deptId);
return R.ok();
}
@Operation(summary = "获取未绑定青龙的部门列表")
@GetMapping("/unbound-departments")
public R unboundDepartments() {
List<QlDeptConfigEntity> boundConfigs = service.list();
List<Long> boundDeptIds = new ArrayList<>();
for (QlDeptConfigEntity c : boundConfigs) {
boundDeptIds.add(c.getDeptId());
}
List<BaseSysDepartmentEntity> allDepts = baseSysDepartmentService.list();
List<JSONObject> unbound = new ArrayList<>();
for (BaseSysDepartmentEntity dept : allDepts) {
if (!boundDeptIds.contains(dept.getId()) && dept.getStatus() == 1) {
JSONObject item = new JSONObject();
item.set("id", dept.getId());
item.set("name", dept.getName());
unbound.add(item);
}
}
return R.ok(unbound);
}
}
@@ -9,15 +9,15 @@ import org.dromara.autotable.annotation.Index;
@Getter
@Setter
@Table(value = "ql_user_config", comment = "青龙用户配置")
public class QlUserConfigEntity extends BaseEntity<QlUserConfigEntity> {
@Table(value = "ql_dept_config", comment = "青龙部门配置")
public class QlDeptConfigEntity extends BaseEntity<QlDeptConfigEntity> {
@Index
@ColumnDefine(comment = "系统用户ID", notNull = true, type = "bigint")
private Long userId;
@ColumnDefine(comment = "部门ID", notNull = true, type = "bigint")
private Long deptId;
@ColumnDefine(comment = "系统用户名", notNull = true)
private String username;
@ColumnDefine(comment = "部门名称", notNull = true)
private String deptName;
@ColumnDefine(comment = "青龙应用ID")
private Long qlAppId;
@@ -0,0 +1,7 @@
package com.cool.modules.task.mapper;
import com.mybatisflex.core.BaseMapper;
import com.cool.modules.task.entity.QlDeptConfigEntity;
public interface QlDeptConfigMapper extends BaseMapper<QlDeptConfigEntity> {
}
@@ -1,7 +0,0 @@
package com.cool.modules.task.mapper;
import com.mybatisflex.core.BaseMapper;
import com.cool.modules.task.entity.QlUserConfigEntity;
public interface QlUserConfigMapper extends BaseMapper<QlUserConfigEntity> {
}
@@ -1,13 +1,17 @@
package com.cool.modules.task.service;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cool.modules.task.entity.QlUserConfigEntity;
import com.cool.modules.task.entity.QlDeptConfigEntity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -29,7 +33,7 @@ public class QinglongService {
private String adminToken;
private boolean enabled;
private final QlUserConfigService qlUserConfigService;
private final QlDeptConfigService qlDeptConfigService;
private String getAdminInternalToken() {
if (!enabled) return null;
@@ -53,14 +57,6 @@ public class QinglongService {
return null;
}
private String getAdminAccessToken() {
if (!enabled) return null;
if (StrUtil.isNotBlank(adminToken)) {
return adminToken;
}
return doTokenRequest(clientId, clientSecret);
}
private String doTokenRequest(String cId, String cSecret) {
try {
String authUrl = url + "/open/auth/token?client_id=" + cId
@@ -77,18 +73,7 @@ public class QinglongService {
return null;
}
private String authHeader(Long userId) {
String token = getUserAccessToken(userId);
return token != null ? "Bearer " + token : "";
}
public String getUserAccessToken(Long userId) {
if (!enabled || userId == null) return null;
QlUserConfigEntity config = qlUserConfigService.getByUserId(userId);
if (config == null) {
config = ensureUserApp(userId);
if (config == null) return null;
}
private String getDeptAccessToken(QlDeptConfigEntity config) {
if (StrUtil.isNotBlank(config.getAccessToken())
&& config.getTokenExpire() != null
&& System.currentTimeMillis() < config.getTokenExpire()) {
@@ -98,27 +83,40 @@ public class QinglongService {
if (token != null) {
config.setAccessToken(token);
config.setTokenExpire(System.currentTimeMillis() + 86400_000L);
qlUserConfigService.updateById(config);
qlDeptConfigService.updateById(config);
}
return token;
}
public QlUserConfigEntity ensureUserApp(Long userId) {
private String authHeader(String token) {
return StrUtil.isNotBlank(token) ? "Bearer " + token : "";
}
public List<QlDeptConfigEntity> getUserDeptConfigs(List<Long> deptIds) {
if (!enabled || CollUtil.isEmpty(deptIds)) return new ArrayList<>();
return qlDeptConfigService.listByDeptIds(deptIds);
}
public QlDeptConfigEntity ensureDeptApp(Long deptId, String deptName) {
if (!enabled) return null;
QlUserConfigEntity existing = qlUserConfigService.getByUserId(userId);
QlDeptConfigEntity existing = qlDeptConfigService.getByDeptId(deptId);
if (existing != null && StrUtil.isNotBlank(existing.getClientId())) {
if (!deptName.equals(existing.getDeptName())) {
existing.setDeptName(deptName);
qlDeptConfigService.updateById(existing);
}
return existing;
}
String adminToken = getAdminInternalToken();
if (adminToken == null) {
log.error("无法获取管理员token来创建用户青龙应用");
log.error("无法获取管理员token来创建部门青龙应用");
return null;
}
try {
String username = "user_" + userId;
String appName = "dept_" + deptId;
String appsUrl = url + "/api/apps?t=" + System.currentTimeMillis();
JSONObject body = new JSONObject();
body.set("name", username);
body.set("name", appName);
body.set("scopes", new JSONArray().put("crons").put("envs").put("scripts").put("logs"));
HttpResponse resp = HttpRequest.post(appsUrl)
.header("Authorization", "Bearer " + adminToken)
@@ -128,52 +126,84 @@ public class QinglongService {
JSONObject result = JSONUtil.parseObj(resp.body());
if (result.getInt("code") == 200 && result.get("data") != null) {
JSONObject data = result.getJSONObject("data");
QlUserConfigEntity config = existing != null ? existing : new QlUserConfigEntity();
config.setUserId(userId);
config.setUsername(username);
QlDeptConfigEntity config = existing != null ? existing : new QlDeptConfigEntity();
config.setDeptId(deptId);
config.setDeptName(deptName);
config.setQlAppId(data.getLong("id"));
config.setClientId(data.getStr("client_id"));
config.setClientSecret(data.getStr("client_secret"));
if (config.getId() != null) {
qlUserConfigService.updateById(config);
qlDeptConfigService.updateById(config);
} else {
qlUserConfigService.save(config);
qlDeptConfigService.save(config);
}
log.info("为用户{}创建青龙应用成功: clientId={}", userId, config.getClientId());
log.info("为部门{}({})创建青龙应用成功: clientId={}", deptId, deptName, config.getClientId());
return config;
}
log.error("创建青龙应用失败: {}", result.getStr("message"));
log.error("创建部门青龙应用失败: {}", result.getStr("message"));
} catch (Exception e) {
log.error("创建青龙应用异常: {}", e.getMessage());
log.error("创建部门青龙应用异常: {}", e.getMessage());
}
return existing;
}
public JSONObject checkConnection(Long userId) {
public boolean deleteDeptApp(Long deptId) {
if (!enabled) return false;
QlDeptConfigEntity config = qlDeptConfigService.getByDeptId(deptId);
if (config == null) return true;
if (config.getQlAppId() != null) {
String adminToken = getAdminInternalToken();
if (adminToken != null) {
try {
String appsUrl = url + "/api/apps?t=" + System.currentTimeMillis();
JSONArray ids = new JSONArray().put(config.getQlAppId());
HttpRequest.delete(appsUrl)
.header("Authorization", "Bearer " + adminToken)
.header("Content-Type", "application/json")
.body(ids.toString())
.timeout(10000).execute();
log.info("删除部门{}青龙应用成功", deptId);
} catch (Exception e) {
log.error("删除部门青龙应用异常: {}", e.getMessage());
}
}
}
qlDeptConfigService.removeById(config.getId());
return true;
}
public JSONObject checkConnection(List<Long> deptIds) {
JSONObject result = new JSONObject();
if (!enabled) {
result.set("connected", false).set("message", "青龙未启用");
return result;
}
List<QlDeptConfigEntity> configs = getUserDeptConfigs(deptIds);
if (CollUtil.isEmpty(configs)) {
result.set("connected", false).set("message", "当前部门未配置青龙面板");
return result;
}
try {
String t = getUserAccessToken(userId);
if (t != null) {
result.set("connected", true).set("message", "连接成功");
} else {
result.set("connected", false).set("message", "Token获取失败");
for (QlDeptConfigEntity config : configs) {
String t = getDeptAccessToken(config);
if (t == null) {
result.set("connected", false).set("message", "部门[" + config.getDeptName() + "]Token获取失败");
return result;
}
}
result.set("connected", true).set("message", "连接成功");
} catch (Exception e) {
result.set("connected", false).set("message", e.getMessage());
}
return result;
}
private JSONObject qlGet(Long userId, String path) {
private JSONObject qlGet(String token, String path) {
if (!enabled) return new JSONObject().set("code", 500).set("message", "青龙未启用");
try {
String sUrl = url + path + (path.contains("?") ? "&" : "?") + "t=" + System.currentTimeMillis();
HttpResponse resp = HttpRequest.get(sUrl)
.header("Authorization", authHeader(userId))
.header("Authorization", authHeader(token))
.timeout(10000).execute();
return JSONUtil.parseObj(resp.body());
} catch (Exception e) {
@@ -182,12 +212,12 @@ public class QinglongService {
}
}
private JSONObject qlPost(Long userId, String path, Object body) {
private JSONObject qlPost(String token, String path, Object body) {
if (!enabled) return new JSONObject().set("code", 500).set("message", "青龙未启用");
try {
String sUrl = url + path + (path.contains("?") ? "&" : "?") + "t=" + System.currentTimeMillis();
HttpResponse resp = HttpRequest.post(sUrl)
.header("Authorization", authHeader(userId))
.header("Authorization", authHeader(token))
.header("Content-Type", "application/json")
.body(body instanceof String ? (String) body : body.toString())
.timeout(10000).execute();
@@ -198,12 +228,12 @@ public class QinglongService {
}
}
private JSONObject qlPut(Long userId, String path, Object body) {
private JSONObject qlPut(String token, String path, Object body) {
if (!enabled) return new JSONObject().set("code", 500).set("message", "青龙未启用");
try {
String sUrl = url + path + (path.contains("?") ? "&" : "?") + "t=" + System.currentTimeMillis();
HttpResponse resp = HttpRequest.put(sUrl)
.header("Authorization", authHeader(userId))
.header("Authorization", authHeader(token))
.header("Content-Type", "application/json")
.body(body instanceof String ? (String) body : body.toString())
.timeout(10000).execute();
@@ -214,12 +244,12 @@ public class QinglongService {
}
}
private JSONObject qlDelete(Long userId, String path, Object body) {
private JSONObject qlDelete(String token, String path, Object body) {
if (!enabled) return new JSONObject().set("code", 500).set("message", "青龙未启用");
try {
String sUrl = url + path + (path.contains("?") ? "&" : "?") + "t=" + System.currentTimeMillis();
HttpResponse resp = HttpRequest.delete(sUrl)
.header("Authorization", authHeader(userId))
.header("Authorization", authHeader(token))
.header("Content-Type", "application/json")
.body(body instanceof String ? (String) body : body.toString())
.timeout(10000).execute();
@@ -230,98 +260,186 @@ public class QinglongService {
}
}
public JSONObject getScripts(Long userId, String searchValue, int page, int size) {
String path = "/open/scripts";
if (StrUtil.isNotBlank(searchValue)) path += "?searchValue=" + searchValue;
return qlGet(userId, path);
private List<QlDeptConfigEntity> resolveDeptConfigs(List<Long> deptIds) {
List<QlDeptConfigEntity> configs = getUserDeptConfigs(deptIds);
if (CollUtil.isEmpty(configs)) {
throw new RuntimeException("当前部门未配置青龙面板,请联系管理员");
}
return configs;
}
public JSONObject getScript(Long userId, String fileName) {
return qlGet(userId, "/open/scripts/" + fileName);
private JSONArray mergeDataFromDepts(List<QlDeptConfigEntity> configs, String apiPath, String dataKey, String searchValue) {
JSONArray merged = new JSONArray();
for (QlDeptConfigEntity config : configs) {
String token = getDeptAccessToken(config);
if (token == null) continue;
String path = apiPath;
if (StrUtil.isNotBlank(searchValue)) {
path += (path.contains("?") ? "&" : "?") + "searchValue=" + searchValue;
}
JSONObject resp = qlGet(token, path);
if (resp.getInt("code") == 200 && resp.get("data") != null) {
Object data = resp.get("data");
JSONArray items = data instanceof JSONArray ? (JSONArray) data : new JSONArray().put(data);
for (int i = 0; i < items.size(); i++) {
JSONObject item = items.getJSONObject(i);
item.set("_deptId", config.getDeptId());
item.set("_deptName", config.getDeptName());
merged.add(item);
}
}
}
return merged;
}
public JSONObject addScript(Long userId, JSONObject body) {
return qlPost(userId, "/open/scripts", body);
public JSONObject getScripts(List<Long> deptIds, String searchValue, int page, int size) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
if (configs.size() == 1) {
String token = getDeptAccessToken(configs.get(0));
String path = "/open/scripts";
if (StrUtil.isNotBlank(searchValue)) path += "?searchValue=" + searchValue;
return qlGet(token, path);
}
JSONArray merged = mergeDataFromDepts(configs, "/open/scripts", "data", searchValue);
return new JSONObject().set("code", 200).set("data", merged);
}
public JSONObject updateScript(Long userId, String fileName, JSONObject body) {
public JSONObject getScript(List<Long> deptIds, String fileName) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlGet(token, "/open/scripts/" + fileName);
}
public JSONObject addScript(List<Long> deptIds, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPost(token, "/open/scripts", body);
}
public JSONObject updateScript(List<Long> deptIds, String fileName, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
body.set("name", fileName);
return qlPut(userId, "/open/scripts", body);
return qlPut(token, "/open/scripts", body);
}
public JSONObject deleteScript(Long userId, java.util.List<String> fileNames) {
return qlDelete(userId, "/open/scripts", JSONUtil.createArray().put(fileNames));
public JSONObject deleteScript(List<Long> deptIds, java.util.List<String> fileNames) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlDelete(token, "/open/scripts", JSONUtil.createArray().put(fileNames));
}
public JSONObject runScript(Long userId, JSONObject body) {
return qlPost(userId, "/open/scripts/run", body);
public JSONObject runScript(List<Long> deptIds, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPost(token, "/open/scripts/run", body);
}
public JSONObject stopScript(Long userId, JSONObject body) {
return qlPost(userId, "/open/scripts/stop", body);
public JSONObject stopScript(List<Long> deptIds, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPost(token, "/open/scripts/stop", body);
}
public JSONObject getEnvs(Long userId, String searchValue, int page, int size) {
String path = "/open/envs";
if (StrUtil.isNotBlank(searchValue)) path += "?searchValue=" + searchValue;
return qlGet(userId, path);
public JSONObject getEnvs(List<Long> deptIds, String searchValue, int page, int size) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
if (configs.size() == 1) {
String token = getDeptAccessToken(configs.get(0));
String path = "/open/envs";
if (StrUtil.isNotBlank(searchValue)) path += "?searchValue=" + searchValue;
return qlGet(token, path);
}
JSONArray merged = mergeDataFromDepts(configs, "/open/envs", "data", searchValue);
return new JSONObject().set("code", 200).set("data", merged);
}
public JSONObject addEnv(Long userId, JSONArray envs) {
return qlPost(userId, "/open/envs", envs);
public JSONObject addEnv(List<Long> deptIds, JSONArray envs) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPost(token, "/open/envs", envs);
}
public JSONObject updateEnv(Long userId, JSONObject body) {
return qlPut(userId, "/open/envs", body);
public JSONObject updateEnv(List<Long> deptIds, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/envs", body);
}
public JSONObject deleteEnv(Long userId, JSONArray ids) {
return qlDelete(userId, "/open/envs", ids);
public JSONObject deleteEnv(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlDelete(token, "/open/envs", ids);
}
public JSONObject enableEnv(Long userId, JSONArray ids) {
return qlPut(userId, "/open/envs/enable", ids);
public JSONObject enableEnv(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/envs/enable", ids);
}
public JSONObject disableEnv(Long userId, JSONArray ids) {
return qlPut(userId, "/open/envs/disable", ids);
public JSONObject disableEnv(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/envs/disable", ids);
}
public JSONObject getCrons(Long userId, String searchValue, int page, int size) {
String path = "/open/crons";
if (StrUtil.isNotBlank(searchValue)) path += "?searchValue=" + searchValue;
return qlGet(userId, path);
public JSONObject getCrons(List<Long> deptIds, String searchValue, int page, int size) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
if (configs.size() == 1) {
String token = getDeptAccessToken(configs.get(0));
String path = "/open/crons";
if (StrUtil.isNotBlank(searchValue)) path += "?searchValue=" + searchValue;
return qlGet(token, path);
}
JSONArray merged = mergeDataFromDepts(configs, "/open/crons", "data", searchValue);
return new JSONObject().set("code", 200).set("data", merged);
}
public JSONObject addCron(Long userId, JSONObject body) {
return qlPost(userId, "/open/crons", body);
public JSONObject addCron(List<Long> deptIds, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPost(token, "/open/crons", body);
}
public JSONObject updateCron(Long userId, JSONObject body) {
return qlPut(userId, "/open/crons", body);
public JSONObject updateCron(List<Long> deptIds, JSONObject body) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/crons", body);
}
public JSONObject deleteCron(Long userId, JSONArray ids) {
return qlDelete(userId, "/open/crons", ids);
public JSONObject deleteCron(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlDelete(token, "/open/crons", ids);
}
public JSONObject runCron(Long userId, JSONArray ids) {
return qlPut(userId, "/open/crons/run", ids);
public JSONObject runCron(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/crons/run", ids);
}
public JSONObject stopCron(Long userId, JSONArray ids) {
return qlPut(userId, "/open/crons/stop", ids);
public JSONObject stopCron(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/crons/stop", ids);
}
public JSONObject enableCron(Long userId, JSONArray ids) {
return qlPut(userId, "/open/crons/enable", ids);
public JSONObject enableCron(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/crons/enable", ids);
}
public JSONObject disableCron(Long userId, JSONArray ids) {
return qlPut(userId, "/open/crons/disable", ids);
public JSONObject disableCron(List<Long> deptIds, JSONArray ids) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlPut(token, "/open/crons/disable", ids);
}
public JSONObject getCronLog(Long userId, String cronId) {
return qlGet(userId, "/open/crons/" + cronId + "/log");
public JSONObject getCronLog(List<Long> deptIds, String cronId) {
List<QlDeptConfigEntity> configs = resolveDeptConfigs(deptIds);
String token = getDeptAccessToken(configs.get(0));
return qlGet(token, "/open/crons/" + cronId + "/log");
}
}
@@ -0,0 +1,10 @@
package com.cool.modules.task.service;
import com.cool.core.base.BaseService;
import com.cool.modules.task.entity.QlDeptConfigEntity;
import java.util.List;
public interface QlDeptConfigService extends BaseService<QlDeptConfigEntity> {
QlDeptConfigEntity getByDeptId(Long deptId);
List<QlDeptConfigEntity> listByDeptIds(List<Long> deptIds);
}
@@ -1,8 +0,0 @@
package com.cool.modules.task.service;
import com.cool.core.base.BaseService;
import com.cool.modules.task.entity.QlUserConfigEntity;
public interface QlUserConfigService extends BaseService<QlUserConfigEntity> {
QlUserConfigEntity getByUserId(Long userId);
}
@@ -0,0 +1,24 @@
package com.cool.modules.task.service.impl;
import com.cool.core.base.BaseServiceImpl;
import com.cool.modules.task.entity.QlDeptConfigEntity;
import com.cool.modules.task.mapper.QlDeptConfigMapper;
import com.cool.modules.task.service.QlDeptConfigService;
import com.mybatisflex.core.query.QueryWrapper;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class QlDeptConfigServiceImpl extends BaseServiceImpl<QlDeptConfigMapper, QlDeptConfigEntity>
implements QlDeptConfigService {
@Override
public QlDeptConfigEntity getByDeptId(Long deptId) {
return getOne(QueryWrapper.create().eq(QlDeptConfigEntity::getDeptId, deptId));
}
@Override
public List<QlDeptConfigEntity> listByDeptIds(List<Long> deptIds) {
return list(QueryWrapper.create().in(QlDeptConfigEntity::getDeptId, deptIds));
}
}
@@ -1,18 +0,0 @@
package com.cool.modules.task.service.impl;
import com.cool.core.base.BaseServiceImpl;
import com.cool.modules.task.entity.QlUserConfigEntity;
import com.cool.modules.task.mapper.QlUserConfigMapper;
import com.cool.modules.task.service.QlUserConfigService;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
@Service
public class QlUserConfigServiceImpl extends BaseServiceImpl<QlUserConfigMapper, QlUserConfigEntity>
implements QlUserConfigService {
@Override
public QlUserConfigEntity getByUserId(Long userId) {
return getOne(QueryWrapper.create().eq(QlUserConfigEntity::getUserId, userId));
}
}