1、记录文件体积 2、删除文件同步删除云存储上的文件
This commit is contained in:
@@ -40,6 +40,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
.antMatchers(
|
.antMatchers(
|
||||||
"/api/login",
|
"/api/login",
|
||||||
"/api/register",
|
"/api/register",
|
||||||
|
"/api/oss/upload",
|
||||||
"/druid/**",
|
"/druid/**",
|
||||||
"/swagger-ui.html",
|
"/swagger-ui.html",
|
||||||
"/swagger-resources/**",
|
"/swagger-resources/**",
|
||||||
|
|||||||
@@ -73,10 +73,10 @@ public class SocketIOConfig implements InitializingBean {
|
|||||||
config.setKeyStorePassword("123456"); // 设置证书密码
|
config.setKeyStorePassword("123456"); // 设置证书密码
|
||||||
|
|
||||||
// 启动socket服务
|
// 启动socket服务
|
||||||
SocketIOServer server = new SocketIOServer(config);
|
// SocketIOServer server = new SocketIOServer(config);
|
||||||
server.addListeners(socketIOHandler);
|
// server.addListeners(socketIOHandler);
|
||||||
server.start();
|
// server.start();
|
||||||
ClientCache.setSocketIOServer(server);
|
// ClientCache.setSocketIOServer(server);
|
||||||
logger.debug("Netty SocketIO启动:{}:{}",host,port);
|
// logger.debug("Netty SocketIO启动:{}:{}",host,port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,21 @@ import com.aliyuncs.auth.sts.AssumeRoleResponse;
|
|||||||
import com.aliyuncs.http.MethodType;
|
import com.aliyuncs.http.MethodType;
|
||||||
import com.aliyuncs.profile.DefaultProfile;
|
import com.aliyuncs.profile.DefaultProfile;
|
||||||
import com.aliyuncs.profile.IClientProfile;
|
import com.aliyuncs.profile.IClientProfile;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||||
import com.gxwebsoft.common.core.utils.FileServerUtil;
|
import com.gxwebsoft.common.core.utils.FileServerUtil;
|
||||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.system.entity.Company;
|
||||||
import com.gxwebsoft.common.system.entity.FileRecord;
|
import com.gxwebsoft.common.system.entity.FileRecord;
|
||||||
|
import com.gxwebsoft.common.system.service.CompanyService;
|
||||||
import com.gxwebsoft.common.system.service.FileRecordService;
|
import com.gxwebsoft.common.system.service.FileRecordService;
|
||||||
|
import com.gxwebsoft.common.system.service.SettingService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@@ -35,10 +40,12 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阿里云OSS云存储
|
* 阿里云OSS云存储
|
||||||
@@ -56,26 +63,55 @@ public class AliOssController extends BaseController {
|
|||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
@Resource
|
@Resource
|
||||||
private FileRecordService fileRecordService;
|
private FileRecordService fileRecordService;
|
||||||
|
@Resource
|
||||||
|
private CompanyService companyService;
|
||||||
|
@Resource
|
||||||
|
private SettingService settingService;
|
||||||
|
|
||||||
@ApiOperation("上传文件")
|
@ApiOperation("上传文件")
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
public ApiResult<FileRecord> upload(@RequestParam MultipartFile file, HttpServletRequest request) throws Exception{
|
public ApiResult<FileRecord> upload(@RequestParam MultipartFile file, HttpServletRequest request) throws Exception{
|
||||||
FileRecord result = null;
|
// 获取租户ID
|
||||||
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
|
String tenantId = request.getHeader("TenantId");
|
||||||
String endpoint = config.getEndpoint();
|
String companyId = request.getHeader("CompanyId");
|
||||||
// RAM用户的访问密钥(AccessKey ID和AccessKey Secret)。
|
System.out.println("companyId = " + companyId);
|
||||||
String accessKeyId = config.getAccessKeyId();
|
if(StrUtil.isBlank(tenantId)){
|
||||||
String accessKeySecret = config.getAccessKeySecret();
|
return fail("传参错误",null);
|
||||||
// 使用代码嵌入的RAM用户的访问密钥配置访问凭证。
|
}
|
||||||
CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
|
|
||||||
// 填写Bucket名称,例如examplebucket。
|
|
||||||
String bucketName = config.getBucketName();
|
|
||||||
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
|
|
||||||
// String objectName = "exampledir/exampleobject.txt";
|
|
||||||
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
|
|
||||||
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
|
|
||||||
// String filePath= "D:\\localpath\\examplefile.txt";
|
|
||||||
|
|
||||||
|
// 读取配置信息
|
||||||
|
JSONObject settingInfo;
|
||||||
|
String key3 = "Upload:" + tenantId;
|
||||||
|
settingInfo = redisUtil.get(key3, JSONObject.class);
|
||||||
|
if (settingInfo == null) {
|
||||||
|
settingInfo = settingService.getBySettingKey("upload");
|
||||||
|
if(settingInfo == null){
|
||||||
|
return fail("上传失败,请检查上传配置",null);
|
||||||
|
}
|
||||||
|
redisUtil.set(key3,settingInfo);
|
||||||
|
}
|
||||||
|
String endpoint = settingInfo.getString("bucketEndpoint");
|
||||||
|
String bucketDomain = settingInfo.getString("bucketDomain");
|
||||||
|
String bucketName = settingInfo.getString("bucketName");
|
||||||
|
String accessKeyId = settingInfo.getString("accessKeyId");
|
||||||
|
String accessKeySecret = settingInfo.getString("accessKeySecret");
|
||||||
|
|
||||||
|
// 判断是否登录
|
||||||
|
String authorization = getAuthorization();
|
||||||
|
|
||||||
|
// 判断存储空间是否已满
|
||||||
|
String key = "StorageIsFull:" + tenantId;
|
||||||
|
String storageIsFull = redisUtil.get(key);
|
||||||
|
if(StrUtil.isNotBlank(storageIsFull)){
|
||||||
|
// 使用自定义云存储不限制
|
||||||
|
if(bucketName.equals("oss-gxwebsoft")){
|
||||||
|
return fail("存储空间已满", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件结果
|
||||||
|
FileRecord result;
|
||||||
|
CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
|
||||||
// 创建OSSClient实例。
|
// 创建OSSClient实例。
|
||||||
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
|
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
|
||||||
|
|
||||||
@@ -98,30 +134,34 @@ public class AliOssController extends BaseController {
|
|||||||
PutObjectResult ossResult = ossClient.putObject(putObjectRequest);
|
PutObjectResult ossResult = ossClient.putObject(putObjectRequest);
|
||||||
|
|
||||||
// 保存记录并返回
|
// 保存记录并返回
|
||||||
String requestURL = config.getBucketDomain();
|
result = new FileRecord();
|
||||||
final String domain = redisUtil.getUploadConfig(getTenantId()).get("bucketDomain");
|
if(StrUtil.isNotBlank(authorization)){
|
||||||
if(StrUtil.isNotBlank(domain)){
|
result.setCreateUserId(getLoginUserId());
|
||||||
requestURL = domain;
|
}
|
||||||
|
if(StrUtil.isNotBlank(companyId)){
|
||||||
|
result.setCompanyId(Integer.valueOf(companyId));
|
||||||
}
|
}
|
||||||
path = "/".concat(path);
|
path = "/".concat(path);
|
||||||
final String cache = redisUtil.get("setting:upload:" + getTenantId());
|
|
||||||
final JSONObject jsonObject = JSONObject.parseObject(cache);
|
|
||||||
final String bucketDomain = jsonObject.getString("bucketDomain");
|
|
||||||
if(StrUtil.isNotBlank(bucketDomain)){
|
|
||||||
requestURL = bucketDomain;
|
|
||||||
}
|
|
||||||
result = new FileRecord();
|
|
||||||
result.setCreateUserId(getLoginUserId());
|
|
||||||
result.setName(StrUtil.isBlank(originalName) ? upload.getName() : originalName);
|
result.setName(StrUtil.isBlank(originalName) ? upload.getName() : originalName);
|
||||||
result.setLength(upload.length());
|
result.setLength(upload.length());
|
||||||
result.setPath(requestURL + path);
|
result.setPath(bucketDomain + path);
|
||||||
result.setThumbnail(requestURL + path + "?x-oss-process=image/resize,m_fixed,w_100,h_100/quality,Q_90");
|
result.setThumbnail(bucketDomain + path + "?x-oss-process=image/resize,m_fixed,w_100,h_100/quality,Q_90");
|
||||||
result.setUrl(requestURL + path + "?x-oss-process=image/resize,w_750/quality,Q_90");
|
result.setUrl(bucketDomain + path + "?x-oss-process=image/resize,w_750/quality,Q_90");
|
||||||
result.setDownloadUrl(requestURL + path);
|
result.setDownloadUrl(bucketDomain + path);
|
||||||
String contentType = FileServerUtil.getContentType(upload);
|
String contentType = FileServerUtil.getContentType(upload);
|
||||||
result.setContentType(contentType);
|
result.setContentType(contentType);
|
||||||
fileRecordService.save(result);
|
result.setTenantId(Integer.valueOf(tenantId));
|
||||||
upload.delete();
|
upload.delete();
|
||||||
|
fileRecordService.save(result);
|
||||||
|
// 更新存储空间
|
||||||
|
if(companyId != null){
|
||||||
|
Company company = companyService.getById(Integer.valueOf(companyId));
|
||||||
|
company.setStorage(company.getStorage() + result.getLength());
|
||||||
|
if(company.getStorage().compareTo(company.getStorageMax()) > 0){
|
||||||
|
redisUtil.set(key,1);
|
||||||
|
}
|
||||||
|
companyService.updateById(company);
|
||||||
|
}
|
||||||
return success(result);
|
return success(result);
|
||||||
|
|
||||||
} catch (OSSException oe) {
|
} catch (OSSException oe) {
|
||||||
@@ -144,7 +184,6 @@ public class AliOssController extends BaseController {
|
|||||||
return fail("上传失败", null);
|
return fail("上传失败", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@OperationLog
|
@OperationLog
|
||||||
@ApiOperation("获取临时osstoken")
|
@ApiOperation("获取临时osstoken")
|
||||||
@GetMapping("/getSTSToken")
|
@GetMapping("/getSTSToken")
|
||||||
@@ -218,7 +257,7 @@ public class AliOssController extends BaseController {
|
|||||||
|
|
||||||
|
|
||||||
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
|
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
|
||||||
byte[] binaryData = postPolicy.getBytes("utf-8");
|
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
|
||||||
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
|
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
|
||||||
String postSignature = ossClient.calculatePostSignature(postPolicy);
|
String postSignature = ossClient.calculatePostSignature(postPolicy);
|
||||||
Map result = new HashMap<>();
|
Map result = new HashMap<>();
|
||||||
@@ -226,9 +265,6 @@ public class AliOssController extends BaseController {
|
|||||||
result.put("signature",postSignature);
|
result.put("signature",postSignature);
|
||||||
result.put("expireEndTime",expireEndTime);
|
result.put("expireEndTime",expireEndTime);
|
||||||
return success(result);
|
return success(result);
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return fail();
|
|
||||||
} finally {
|
} finally {
|
||||||
ossClient.shutdown();
|
ossClient.shutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import com.gxwebsoft.common.core.utils.RedisUtil;
|
|||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.system.entity.Company;
|
||||||
import com.gxwebsoft.common.system.entity.FileRecord;
|
import com.gxwebsoft.common.system.entity.FileRecord;
|
||||||
import com.gxwebsoft.common.system.param.FileRecordParam;
|
import com.gxwebsoft.common.system.param.FileRecordParam;
|
||||||
|
import com.gxwebsoft.common.system.service.CompanyService;
|
||||||
import com.gxwebsoft.common.system.service.FileRecordService;
|
import com.gxwebsoft.common.system.service.FileRecordService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
@@ -49,6 +51,8 @@ public class FileController extends BaseController {
|
|||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
@Resource
|
@Resource
|
||||||
private FileRecordService fileRecordService;
|
private FileRecordService fileRecordService;
|
||||||
|
@Resource
|
||||||
|
private CompanyService companyService;
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:file:upload')")
|
@PreAuthorize("hasAuthority('sys:file:upload')")
|
||||||
@OperationLog
|
@OperationLog
|
||||||
@@ -213,6 +217,15 @@ public class FileController extends BaseController {
|
|||||||
new File(getUploadSmDir(), record.getPath())
|
new File(getUploadSmDir(), record.getPath())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
// 释放空间大小
|
||||||
|
System.out.println("record = " + record);
|
||||||
|
if(record.getCompanyId() > 0){
|
||||||
|
Company company = companyService.getById(record.getCompanyId());
|
||||||
|
company.setStorage(company.getStorage() - record.getLength());
|
||||||
|
companyService.updateById(company);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除远程文件
|
||||||
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
|
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
|
||||||
String endpoint = config.getEndpoint();
|
String endpoint = config.getEndpoint();
|
||||||
String accessKeyId = config.getAccessKeyId();
|
String accessKeyId = config.getAccessKeyId();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.gxwebsoft.common.core.web.ExistenceParam;
|
import com.gxwebsoft.common.core.web.ExistenceParam;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
import com.gxwebsoft.common.system.entity.Company;
|
import com.gxwebsoft.common.system.entity.Company;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.common.system.param.CompanyParam;
|
import com.gxwebsoft.common.system.param.CompanyParam;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@@ -45,4 +46,7 @@ public interface CompanyMapper extends BaseMapper<Company> {
|
|||||||
|
|
||||||
@InterceptorIgnore(tenantLine = "true")
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
Company getCompanyAll(@Param("companyId") Integer companyId);
|
Company getCompanyAll(@Param("companyId") Integer companyId);
|
||||||
|
|
||||||
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
|
void updateByCompanyId(@Param("param") Company company);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,4 +152,9 @@
|
|||||||
<include refid="selectSql"></include>
|
<include refid="selectSql"></include>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 更新企业信息-->
|
||||||
|
<update id="updateByCompanyId">
|
||||||
|
UPDATE sys_company SET storage = #{param.storage} WHERE company_id = #{param.companyId}
|
||||||
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -43,4 +43,6 @@ public interface CompanyService extends IService<Company> {
|
|||||||
Company getByTenantIdRel(Integer tenantId);
|
Company getByTenantIdRel(Integer tenantId);
|
||||||
|
|
||||||
PageResult<Company> pageRelAll(CompanyParam param);
|
PageResult<Company> pageRelAll(CompanyParam param);
|
||||||
|
|
||||||
|
void updateByCompanyId(Company company);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.gxwebsoft.common.core.web.PageResult;
|
|||||||
import com.gxwebsoft.common.system.entity.FileRecord;
|
import com.gxwebsoft.common.system.entity.FileRecord;
|
||||||
import com.gxwebsoft.common.system.param.FileRecordParam;
|
import com.gxwebsoft.common.system.param.FileRecordParam;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -54,5 +55,4 @@ public interface FileRecordService extends IService<FileRecord> {
|
|||||||
* @param files 文件数组
|
* @param files 文件数组
|
||||||
*/
|
*/
|
||||||
void deleteFileAsync(List<File> files);
|
void deleteFileAsync(List<File> files);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,4 +61,9 @@ public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> impl
|
|||||||
return new PageResult<>(list, page.getTotal());
|
return new PageResult<>(list, page.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateByCompanyId(Company company) {
|
||||||
|
baseMapper.updateByCompanyId(company);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
package com.gxwebsoft.common.system.service.impl;
|
package com.gxwebsoft.common.system.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.gxwebsoft.common.core.utils.CommonUtil;
|
import com.gxwebsoft.common.core.utils.CommonUtil;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.system.entity.Company;
|
||||||
import com.gxwebsoft.common.system.entity.FileRecord;
|
import com.gxwebsoft.common.system.entity.FileRecord;
|
||||||
import com.gxwebsoft.common.system.mapper.FileRecordMapper;
|
import com.gxwebsoft.common.system.mapper.FileRecordMapper;
|
||||||
import com.gxwebsoft.common.system.param.FileRecordParam;
|
import com.gxwebsoft.common.system.param.FileRecordParam;
|
||||||
|
import com.gxwebsoft.common.system.service.CompanyService;
|
||||||
import com.gxwebsoft.common.system.service.FileRecordService;
|
import com.gxwebsoft.common.system.service.FileRecordService;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -22,6 +27,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class FileRecordServiceImpl extends ServiceImpl<FileRecordMapper, FileRecord> implements FileRecordService {
|
public class FileRecordServiceImpl extends ServiceImpl<FileRecordMapper, FileRecord> implements FileRecordService {
|
||||||
|
@Resource
|
||||||
|
private CompanyService companyService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<FileRecord> pageRel(FileRecordParam param) {
|
public PageResult<FileRecord> pageRel(FileRecordParam param) {
|
||||||
|
|||||||
Reference in New Issue
Block a user