feat(app): 添加开发者资源管理系统
- 创建app_resource数据表,支持服务器/数据库/云存储/域名/SSL证书等资源管理 - 实现AppResource实体类,包含各类资源的特定字段和通用字段 - 开发AppResourceController控制器,提供资源的增删改查和统计功能 - 实现AppResourceService服务层,包含业务逻辑和数据操作方法 - 创建AppResourceMapper数据访问层,支持分页查询和关联查询 - 添加AppResourceParam查询参数类,支持多条件筛选 - 集成MyBatis XML映射文件,实现复杂的关联查询和统计功能 - 实现基于用户权限的资源访问控制和逻辑删除机制
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package com.gxwebsoft.app.controller;
|
||||
|
||||
import com.gxwebsoft.app.entity.AppResource;
|
||||
import com.gxwebsoft.app.param.AppResourceParam;
|
||||
import com.gxwebsoft.app.service.AppResourceService;
|
||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 开发者资源管理控制器(服务器/数据库/云存储/域名/SSL)
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-03-31
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "开发者资源管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/_modules/app/developer-resource")
|
||||
public class AppResourceController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private AppResourceService appResourceService;
|
||||
|
||||
// ─── 查询接口 ─────────────────────────────────────────────────
|
||||
|
||||
@Operation(summary = "分页查询资源列表")
|
||||
@GetMapping("/page")
|
||||
public ApiResult<PageResult<AppResource>> page(AppResourceParam param) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录", null);
|
||||
// 普通开发者只能查自己的资源
|
||||
param.setUserId(loginUser.getUserId());
|
||||
param.setTenantId(loginUser.getTenantId());
|
||||
return success(appResourceService.pageRel(param));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询资源列表(不分页)")
|
||||
@GetMapping
|
||||
public ApiResult<List<AppResource>> list(AppResourceParam param) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录", null);
|
||||
param.setUserId(loginUser.getUserId());
|
||||
param.setTenantId(loginUser.getTenantId());
|
||||
return success(appResourceService.listRel(param));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取资源详情")
|
||||
@GetMapping("/{resourceId}")
|
||||
public ApiResult<AppResource> get(@PathVariable Long resourceId) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录", null);
|
||||
AppResource resource = appResourceService.getByIdRel(resourceId);
|
||||
if (resource == null) return fail("资源不存在", null);
|
||||
if (!resource.getUserId().equals(loginUser.getUserId())) return fail("无权访问此资源", null);
|
||||
return success(resource);
|
||||
}
|
||||
|
||||
@Operation(summary = "统计各类型资源数量")
|
||||
@GetMapping("/stats")
|
||||
public ApiResult<Map<String, Long>> stats() {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录", null);
|
||||
return success(appResourceService.countByType(loginUser.getUserId(), loginUser.getTenantId()));
|
||||
}
|
||||
|
||||
// ─── 新增/修改接口 ────────────────────────────────────────────
|
||||
|
||||
@OperationLog
|
||||
@Operation(summary = "新增资源")
|
||||
@PostMapping
|
||||
public ApiResult<AppResource> save(@RequestBody AppResource resource) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录", null);
|
||||
if (resource.getResourceType() == null || resource.getResourceType().isEmpty()) {
|
||||
return fail("资源类型不能为空", null);
|
||||
}
|
||||
if (resource.getName() == null || resource.getName().isEmpty()) {
|
||||
return fail("资源名称不能为空", null);
|
||||
}
|
||||
resource.setTenantId(loginUser.getTenantId());
|
||||
try {
|
||||
AppResource result = appResourceService.addResource(resource, loginUser.getUserId());
|
||||
return success("添加成功", result);
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@OperationLog
|
||||
@Operation(summary = "修改资源")
|
||||
@PutMapping
|
||||
public ApiResult<AppResource> update(@RequestBody AppResource resource) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录", null);
|
||||
try {
|
||||
AppResource result = appResourceService.updateResource(resource);
|
||||
return success("修改成功", result);
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 删除接口 ─────────────────────────────────────────────────
|
||||
|
||||
@OperationLog
|
||||
@Operation(summary = "删除资源(逻辑删除)")
|
||||
@DeleteMapping("/{resourceId}")
|
||||
public ApiResult<?> remove(@PathVariable Long resourceId) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录");
|
||||
try {
|
||||
appResourceService.removeResource(resourceId, loginUser.getUserId());
|
||||
return success("删除成功");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@OperationLog
|
||||
@Operation(summary = "批量删除资源")
|
||||
@DeleteMapping("/batch")
|
||||
public ApiResult<?> removeBatch(@RequestBody List<Long> ids) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) return fail("请先登录");
|
||||
try {
|
||||
for (Long id : ids) {
|
||||
appResourceService.removeResource(id, loginUser.getUserId());
|
||||
}
|
||||
return success("批量删除成功");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
121
src/main/java/com/gxwebsoft/app/entity/AppResource.java
Normal file
121
src/main/java/com/gxwebsoft/app/entity/AppResource.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package com.gxwebsoft.app.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 开发者资源(服务器/数据库/云存储/域名/SSL证书)
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-03-31
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("app_resource")
|
||||
@Schema(name = "AppResource对象", description = "开发者资源")
|
||||
public class AppResource implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "资源ID")
|
||||
@TableId(value = "resource_id", type = IdType.AUTO)
|
||||
private Long resourceId;
|
||||
|
||||
@Schema(description = "资源类型: server/database/storage/domain/ssl")
|
||||
private String resourceType;
|
||||
|
||||
@Schema(description = "资源名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "服务商: tencent/aliyun/huawei/other")
|
||||
private String provider;
|
||||
|
||||
@Schema(description = "关联应用ID(可选)")
|
||||
private Long websiteId;
|
||||
|
||||
@Schema(description = "关联应用名称(冗余)")
|
||||
private String websiteName;
|
||||
|
||||
// ─── 服务器字段 ───────────────────────────────────────
|
||||
@Schema(description = "IP地址(服务器用)")
|
||||
private String ip;
|
||||
|
||||
// ─── 数据库字段 ───────────────────────────────────────
|
||||
@Schema(description = "数据库类型: MySQL/PostgreSQL/Redis/MongoDB(数据库用)")
|
||||
private String dbType;
|
||||
|
||||
@Schema(description = "连接主机地址(数据库用)")
|
||||
private String host;
|
||||
|
||||
@Schema(description = "连接端口(数据库用)")
|
||||
private Integer port;
|
||||
|
||||
// ─── 云存储字段 ───────────────────────────────────────
|
||||
@Schema(description = "地区/Region(云存储用)")
|
||||
private String region;
|
||||
|
||||
@Schema(description = "访问权限: public-read/private(云存储用)")
|
||||
private String acl;
|
||||
|
||||
@Schema(description = "已用空间(字节,云存储用)")
|
||||
private Long usedBytes;
|
||||
|
||||
// ─── 域名字段 ─────────────────────────────────────────
|
||||
@Schema(description = "域名(域名用)")
|
||||
private String domain;
|
||||
|
||||
@Schema(description = "注册商(域名用)")
|
||||
private String registrar;
|
||||
|
||||
@Schema(description = "是否已备案(域名用)")
|
||||
private Boolean icp;
|
||||
|
||||
@Schema(description = "ICP备案号(域名用)")
|
||||
private String icpNo;
|
||||
|
||||
@Schema(description = "是否已绑定SSL(域名用,冗余)")
|
||||
private Boolean sslBound;
|
||||
|
||||
// ─── SSL证书字段 ──────────────────────────────────────
|
||||
@Schema(description = "证书类型: DV/OV/EV(SSL用)")
|
||||
private String certType;
|
||||
|
||||
@Schema(description = "颁发机构(SSL用)")
|
||||
private String issuer;
|
||||
|
||||
// ─── 通用字段 ─────────────────────────────────────────
|
||||
@Schema(description = "状态: running/stopped/expired/pending")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "到期时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate expireAt;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "所属用户ID")
|
||||
private Integer userId;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
|
||||
@Schema(description = "是否删除: 0否 1是")
|
||||
private Integer deleted;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.gxwebsoft.app.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.gxwebsoft.app.entity.AppResource;
|
||||
import com.gxwebsoft.app.param.AppResourceParam;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 开发者资源 Mapper
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-03-31
|
||||
*/
|
||||
public interface AppResourceMapper extends BaseMapper<AppResource> {
|
||||
|
||||
/**
|
||||
* 分页查询(关联应用名称)
|
||||
*/
|
||||
List<AppResource> selectPageRel(@Param("page") IPage<AppResource> page,
|
||||
@Param("param") AppResourceParam param);
|
||||
|
||||
/**
|
||||
* 查询全部列表(关联应用名称)
|
||||
*/
|
||||
List<AppResource> selectListRel(@Param("param") AppResourceParam param);
|
||||
|
||||
/**
|
||||
* 统计各类型资源数量,返回 [{resourceType, cnt}]
|
||||
*/
|
||||
List<java.util.Map<String, Object>> countByType(@Param("userId") Integer userId,
|
||||
@Param("tenantId") Integer tenantId);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.gxwebsoft.app.mapper.AppResourceMapper">
|
||||
|
||||
<!-- 关联查询 SQL -->
|
||||
<sql id="selectSql">
|
||||
SELECT a.*, w.website_name
|
||||
FROM app_resource a
|
||||
LEFT JOIN cms_website w ON a.website_id = w.website_id AND w.deleted = 0
|
||||
<where>
|
||||
a.deleted = 0
|
||||
<if test="param.resourceId != null">
|
||||
AND a.resource_id = #{param.resourceId}
|
||||
</if>
|
||||
<if test="param.resourceType != null and param.resourceType != ''">
|
||||
AND a.resource_type = #{param.resourceType}
|
||||
</if>
|
||||
<if test="param.websiteId != null">
|
||||
AND a.website_id = #{param.websiteId}
|
||||
</if>
|
||||
<if test="param.provider != null and param.provider != ''">
|
||||
AND a.provider = #{param.provider}
|
||||
</if>
|
||||
<if test="param.status != null and param.status != ''">
|
||||
AND a.status = #{param.status}
|
||||
</if>
|
||||
<if test="param.userId != null">
|
||||
AND a.user_id = #{param.userId}
|
||||
</if>
|
||||
<if test="param.tenantId != null">
|
||||
AND a.tenant_id = #{param.tenantId}
|
||||
</if>
|
||||
<if test="param.keywords != null and param.keywords != ''">
|
||||
AND (
|
||||
a.name LIKE CONCAT('%', #{param.keywords}, '%')
|
||||
OR a.ip LIKE CONCAT('%', #{param.keywords}, '%')
|
||||
OR a.domain LIKE CONCAT('%', #{param.keywords}, '%')
|
||||
OR a.host LIKE CONCAT('%', #{param.keywords}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="param.createTimeStart != null">
|
||||
AND a.create_time >= #{param.createTimeStart}
|
||||
</if>
|
||||
<if test="param.createTimeEnd != null">
|
||||
AND a.create_time <= #{param.createTimeEnd}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY a.create_time DESC
|
||||
</sql>
|
||||
|
||||
<!-- 分页查询 -->
|
||||
<select id="selectPageRel" resultType="com.gxwebsoft.app.entity.AppResource">
|
||||
<include refid="selectSql"/>
|
||||
</select>
|
||||
|
||||
<!-- 查询全部 -->
|
||||
<select id="selectListRel" resultType="com.gxwebsoft.app.entity.AppResource">
|
||||
<include refid="selectSql"/>
|
||||
</select>
|
||||
|
||||
<!-- 按类型统计数量 -->
|
||||
<select id="countByType" resultType="java.util.Map">
|
||||
SELECT resource_type AS resourceType, COUNT(*) AS cnt
|
||||
FROM app_resource
|
||||
WHERE deleted = 0
|
||||
<if test="userId != null">
|
||||
AND user_id = #{userId}
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
AND tenant_id = #{tenantId}
|
||||
</if>
|
||||
GROUP BY resource_type
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
54
src/main/java/com/gxwebsoft/app/param/AppResourceParam.java
Normal file
54
src/main/java/com/gxwebsoft/app/param/AppResourceParam.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package com.gxwebsoft.app.param;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.gxwebsoft.common.core.annotation.QueryField;
|
||||
import com.gxwebsoft.common.core.annotation.QueryType;
|
||||
import com.gxwebsoft.common.core.web.BaseParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 开发者资源查询参数
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-03-31
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(name = "AppResourceParam对象", description = "开发者资源查询参数")
|
||||
public class AppResourceParam extends BaseParam {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "资源ID")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Long resourceId;
|
||||
|
||||
@Schema(description = "资源类型: server/database/storage/domain/ssl")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private String resourceType;
|
||||
|
||||
@Schema(description = "关联应用ID")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Long websiteId;
|
||||
|
||||
@Schema(description = "服务商")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private String provider;
|
||||
|
||||
@Schema(description = "状态")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private String status;
|
||||
|
||||
@Schema(description = "所属用户ID")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer userId;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer tenantId;
|
||||
|
||||
@Schema(description = "关键词(名称/IP/域名/Host模糊搜索)")
|
||||
private String keywords;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.gxwebsoft.app.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.gxwebsoft.app.entity.AppResource;
|
||||
import com.gxwebsoft.app.param.AppResourceParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 开发者资源 Service
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-03-31
|
||||
*/
|
||||
public interface AppResourceService extends IService<AppResource> {
|
||||
|
||||
/** 分页查询(关联应用名称) */
|
||||
PageResult<AppResource> pageRel(AppResourceParam param);
|
||||
|
||||
/** 列表查询 */
|
||||
List<AppResource> listRel(AppResourceParam param);
|
||||
|
||||
/** 详情(关联查询) */
|
||||
AppResource getByIdRel(Long resourceId);
|
||||
|
||||
/** 新增资源 */
|
||||
AppResource addResource(AppResource resource, Integer userId);
|
||||
|
||||
/** 更新资源 */
|
||||
AppResource updateResource(AppResource resource);
|
||||
|
||||
/** 删除资源(逻辑删除) */
|
||||
void removeResource(Long resourceId, Integer userId);
|
||||
|
||||
/** 按资源类型统计数量 [{resourceType, cnt}] */
|
||||
Map<String, Long> countByType(Integer userId, Integer tenantId);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.gxwebsoft.app.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.gxwebsoft.app.entity.AppResource;
|
||||
import com.gxwebsoft.app.mapper.AppResourceMapper;
|
||||
import com.gxwebsoft.app.param.AppResourceParam;
|
||||
import com.gxwebsoft.app.service.AppResourceService;
|
||||
import com.gxwebsoft.common.core.web.PageParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 开发者资源 Service 实现
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-03-31
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AppResourceServiceImpl extends ServiceImpl<AppResourceMapper, AppResource>
|
||||
implements AppResourceService {
|
||||
|
||||
@Override
|
||||
public PageResult<AppResource> pageRel(AppResourceParam param) {
|
||||
PageParam<AppResource, AppResourceParam> page = new PageParam<>(param);
|
||||
page.setDefaultOrder("create_time desc");
|
||||
List<AppResource> list = baseMapper.selectPageRel(page, param);
|
||||
return new PageResult<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AppResource> listRel(AppResourceParam param) {
|
||||
return baseMapper.selectListRel(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppResource getByIdRel(Long resourceId) {
|
||||
AppResourceParam param = new AppResourceParam();
|
||||
param.setResourceId(resourceId);
|
||||
List<AppResource> list = baseMapper.selectListRel(param);
|
||||
return list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AppResource addResource(AppResource resource, Integer userId) {
|
||||
resource.setUserId(userId);
|
||||
resource.setDeleted(0);
|
||||
resource.setCreateTime(LocalDateTime.now());
|
||||
resource.setUpdateTime(LocalDateTime.now());
|
||||
// 默认状态
|
||||
if (resource.getStatus() == null) {
|
||||
resource.setStatus("running");
|
||||
}
|
||||
save(resource);
|
||||
log.info("新增资源成功, type={}, name={}, userId={}", resource.getResourceType(), resource.getName(), userId);
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AppResource updateResource(AppResource resource) {
|
||||
if (resource.getResourceId() == null) {
|
||||
throw new RuntimeException("资源ID不能为空");
|
||||
}
|
||||
resource.setUpdateTime(LocalDateTime.now());
|
||||
updateById(resource);
|
||||
return getByIdRel(resource.getResourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeResource(Long resourceId, Integer userId) {
|
||||
AppResource resource = getById(resourceId);
|
||||
if (resource == null) {
|
||||
throw new RuntimeException("资源不存在");
|
||||
}
|
||||
if (!resource.getUserId().equals(userId)) {
|
||||
throw new RuntimeException("无权操作此资源");
|
||||
}
|
||||
resource.setDeleted(1);
|
||||
resource.setUpdateTime(LocalDateTime.now());
|
||||
updateById(resource);
|
||||
log.info("删除资源成功, resourceId={}, userId={}", resourceId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> countByType(Integer userId, Integer tenantId) {
|
||||
List<Map<String, Object>> raw = baseMapper.countByType(userId, tenantId);
|
||||
Map<String, Long> result = new HashMap<>();
|
||||
// 初始化所有类型为 0
|
||||
for (String type : new String[]{"server", "database", "storage", "domain", "ssl"}) {
|
||||
result.put(type, 0L);
|
||||
}
|
||||
for (Map<String, Object> row : raw) {
|
||||
String type = (String) row.get("resourceType");
|
||||
Long cnt = ((Number) row.get("cnt")).longValue();
|
||||
result.put(type, cnt);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
54
src/main/java/com/gxwebsoft/app/sql/app_resource.sql
Normal file
54
src/main/java/com/gxwebsoft/app/sql/app_resource.sql
Normal file
@@ -0,0 +1,54 @@
|
||||
-- ----------------------------
|
||||
-- 开发者资源管理表
|
||||
-- 统一存放服务器/数据库/云存储/域名/SSL证书等开发基础设施资源
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS `app_resource` (
|
||||
`resource_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '资源ID',
|
||||
`resource_type` VARCHAR(20) NOT NULL COMMENT '资源类型: server/database/storage/domain/ssl',
|
||||
`name` VARCHAR(100) NOT NULL COMMENT '资源名称',
|
||||
`provider` VARCHAR(30) DEFAULT NULL COMMENT '服务商: tencent/aliyun/huawei/other',
|
||||
|
||||
-- 服务器字段
|
||||
`ip` VARCHAR(50) DEFAULT NULL COMMENT 'IP地址(服务器用)',
|
||||
|
||||
-- 数据库字段
|
||||
`db_type` VARCHAR(30) DEFAULT NULL COMMENT '数据库类型: MySQL/PostgreSQL/Redis/MongoDB',
|
||||
`host` VARCHAR(200) DEFAULT NULL COMMENT '连接主机地址(数据库用)',
|
||||
`port` INT DEFAULT NULL COMMENT '连接端口(数据库用)',
|
||||
|
||||
-- 云存储字段
|
||||
`region` VARCHAR(50) DEFAULT NULL COMMENT '地区/Region(云存储用)',
|
||||
`acl` VARCHAR(30) DEFAULT 'private' COMMENT '访问权限: public-read/private(云存储用)',
|
||||
`used_bytes` BIGINT DEFAULT 0 COMMENT '已用空间(字节,云存储用)',
|
||||
|
||||
-- 域名字段
|
||||
`domain` VARCHAR(200) DEFAULT NULL COMMENT '域名(域名/SSL用)',
|
||||
`registrar` VARCHAR(100) DEFAULT NULL COMMENT '注册商(域名用)',
|
||||
`icp` TINYINT(1) DEFAULT 0 COMMENT '是否已备案: 0否 1是(域名用)',
|
||||
`icp_no` VARCHAR(100) DEFAULT NULL COMMENT 'ICP备案号(域名用)',
|
||||
`ssl_bound` TINYINT(1) DEFAULT 0 COMMENT '是否已绑定SSL(域名冗余标记)',
|
||||
|
||||
-- SSL证书字段
|
||||
`cert_type` VARCHAR(10) DEFAULT NULL COMMENT '证书类型: DV/OV/EV(SSL用)',
|
||||
`issuer` VARCHAR(100) DEFAULT NULL COMMENT '颁发机构(SSL用)',
|
||||
|
||||
-- 通用字段
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'running' COMMENT '状态: running/stopped/expired/pending',
|
||||
`website_id` BIGINT DEFAULT NULL COMMENT '关联应用ID(可选)',
|
||||
`expire_at` DATE DEFAULT NULL COMMENT '到期时间',
|
||||
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
`user_id` INT NOT NULL COMMENT '所属用户ID',
|
||||
`tenant_id` INT DEFAULT NULL COMMENT '租户ID',
|
||||
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除: 0否 1是',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
PRIMARY KEY (`resource_id`),
|
||||
KEY `idx_resource_type` (`resource_type`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_website_id` (`website_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_expire_at` (`expire_at`),
|
||||
KEY `idx_deleted` (`deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开发者资源管理(服务器/数据库/云存储/域名/SSL)';
|
||||
Reference in New Issue
Block a user