!14 存储桶改为private

* 后台 使用许可页面
* 优化:移除API访问地址配置
* 后台、pc、h5 删除无用配置
* docker部署优化
* 2.0 networkMode=bridge
* changelog
* 学员端权限为空报错
* h5 我的页面请求优化
* 缓存查询
* 后台 学员列表报错、线上课-上架时间字段优化
* 后台、pc、h5 使用签名地址
* 学员端接口修改
* 后台、pc 使用签名地址
* 后台 使用签名地址
* 上传接口
* 上传接口
* 系统配置
* 线上课封面
* bucket由public改为private
* 资源相关表实体及对象修改
* 统一数据库脚本
This commit is contained in:
白书科技
2025-05-22 07:23:06 +00:00
parent c206fa4bf2
commit 12daa31ab9
134 changed files with 10054 additions and 1231 deletions

View File

@@ -26,6 +26,7 @@ import xyz.playedu.common.service.AdminPermissionService;
import xyz.playedu.common.service.AdminRoleService;
import xyz.playedu.common.service.AdminUserService;
import xyz.playedu.common.util.PrivacyUtil;
import xyz.playedu.common.util.StringUtil;
@Component
public class BackendBus {
@@ -70,6 +71,9 @@ public class BackendBus {
}
HashMap<String, Boolean> permissions = BCtx.getAdminPer();
if (StringUtil.isNull(permissions)) {
return "";
}
if (permissions.get(permissionSlug) != null) {
return value;
}

View File

@@ -587,7 +587,7 @@ public class LDAPBus {
return;
}
String defaultAvatar = appConfigService.defaultAvatar();
Integer defaultAvatar = appConfigService.defaultAvatar();
for (LdapTransformUser ldapTransformUser : userList) {
if (ldapTransformUser.isBan()) {
@@ -607,7 +607,7 @@ public class LDAPBus {
}
}
public User singleUserSync(LdapTransformUser ldapTransformUser, String defaultAvatar) {
public User singleUserSync(LdapTransformUser ldapTransformUser, Integer defaultAvatar) {
log.info(
"*****START*****LDAP-用户同步-开始|ctx=[dn:{},uuid:{}]",
ldapTransformUser.getDn(),

View File

@@ -27,6 +27,7 @@ public class BackendConstant {
{
add("/backend/v1/system/image-captcha");
add("/backend/v1/auth/login");
add("/backend/v1/cache/list");
}
};
@@ -46,6 +47,7 @@ public class BackendConstant {
public static final String RESOURCE_TYPE_ZIP = "ZIP";
public static final String RESOURCE_TYPE_RAR = "RAR";
public static final String RESOURCE_TYPE_TXT = "TXT";
public static final String RESOURCE_TYPE_OTHER = "OTHER";
public static final String RESOURCE_TYPE_ATTACHMENT =
RESOURCE_TYPE_PDF

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.common.constant;
public class CommonConstant {
public static final Integer MINUS_ONE = -1;
public static final Integer ZERO = 0;
public static final Integer ONE = 1;
public static final Integer TWO = 2;
public static final Integer THREE = 3;
public static final Integer FOUR = 4;
public static final Integer FIVE = 5;
public static final Integer SIX = 6;
public static final Integer SEVEN = 7;
public static final Integer EIGHT = 8;
public static final Integer TEN = 10;
public static final Integer ELEVEN = 11;
public static final Integer TWELVE = 12;
public static final Integer FIFTEEN = 15;
public static final Integer TWENTY_ONE = 21;
public static final Integer TWENTY_TWO = 22;
public static final Integer TWENTY_FIVE = 25;
public static final Integer THIRTY = 30;
public static final Integer THIRTY_ONE = 31;
public static final Integer THIRTY_TWO = 32;
public static final Integer THIRTY_FIVE = 35;
public static final Integer FORTY_ONE = 41;
public static final Integer FORTY_TWO = 42;
public static final Integer FORTY_FIVE = 45;
public static final Integer SIXTY = 60;
public static final Integer ONE_HUNDRED = 100;
public static final String SUCCESS = "0";
public static final String FAIL = "-1";
public static final String UTF8 = "UTF-8";
public static final String GLOBAL = "GLOBAL";
public static final String COURSE = "COURSE";
public static final String OFFLINE_COURSE = "OFFLINE_COURSE";
public static final String REQUIRED_COURSE = "REQUIRED_COURSE";
public static final String OPTIONAL_COURSE = "OPTIONAL_COURSE";
public static final String STUDY_TASK = "STUDY_TASK";
public static final String EXAM_TASK = "EXAM_TASK";
public static final String LOGIN = "LOGIN";
public static final String MANUAL = "MANUAL";
public static final String SHOP = "SHOP";
public static final String SHOP_ROLLBACK = "SHOP_ROLLBACK";
public static final String MANUAL_TIP = "手动调整";
public static final String LOGIN_TIP = "首次登录";
public static final String SHOP_TIP = "积分商城购物";
public static final String SHOP_ROLLBACK_TIP = "积分商城退还积分";
public static final String HOUR_FINISHED_TIP = "课时学习完成";
public static final String COURSE_FINISHED_TIP = "线上课程学习完成";
public static final String OFFLINE_COURSE_FINISHED_TIP = "线下课程签到完成";
public static final String EXAM_FINISHED_TIP = "考试首次合格";
public static final String TASK_EXAM_FINISHED_TIP = "考试任务首次合格";
public static final String TASK_STUDY_FINISHED_TIP = "学习任务学习完成";
public static final String EXAM = "EXAM";
public static final String EXAM_RANGE_BEGIN = "EXAM_RANGE_BEGIN";
public static final String EXAM_RANGE_END = "EXAM_RANGE_END";
public static final String STUDY = "STUDY";
public static final String STUDY_RANGE = "STUDY_RANGE";
public static final String RETAKE = "RETAKE";
public static final String USER_CREATE = "USER_CREATE";
public static final String OTHER = "OTHER";
public static final String TRANSCODE = "document-transcode/";
public static final String SEPARATOR = ".";
public static final String JSON = "json";
public static final String OTHER_DEP = "其他(待设置部门)";
public static final Integer LOGIN_CHANNEL_LOCAL = 0;
public static final Integer LOGIN_CHANNEL_WORK_WECHAT = 1;
public static final Integer LOGIN_CHANNEL_FEISHU = 2;
public static final Integer LOGIN_CHANNEL_DINGTALK = 3;
public static final Integer LOGIN_CHANNEL_LDAP = 4;
public static final Integer LOGIN_CHANNEL_YUNZHIJIA = 7;
public static final Integer LOGIN_CHANNEL_CREC = 8;
public static final int TYPE_DEP = 0;
public static final int TYPE_USER = 1;
public static final int TYPE_GROUP = 2;
public static final String OIDC_CLIENT_SECRET_POST = "client_secret_post";
public static final String OIDC_CLIENT_SECRET_BASIC = "client_secret_basic";
public static final String OIDC_NONE = "none";
public static final String EXPORT_ALL_USER_RECORD = "export_all_user_record";
public static final String EXTRA_V = "v1";
public static final String S3_ACCESS_MODE_PATH = "path";
public static final String S3_ACCESS_MODE_VIRTUAL = "virtual";
}

View File

@@ -46,7 +46,7 @@ public class User implements Serializable {
private String name;
/** 头像 */
private String avatar;
private Integer avatar;
/** 密码 */
@JsonIgnore private String password;

View File

@@ -35,9 +35,11 @@ public interface AppConfigService extends IService<AppConfig> {
S3Config getS3Config();
List<Integer> getAllImageValue();
boolean enabledLdapLogin();
String defaultAvatar();
Integer defaultAvatar();
LdapConfig ldapConfig();
}

View File

@@ -47,7 +47,7 @@ public interface UserService extends IService<User> {
User createWithDepIds(
String email,
String name,
String avatar,
Integer avatar,
String password,
String idCard,
Integer[] depIds);
@@ -56,7 +56,7 @@ public interface UserService extends IService<User> {
User user,
String email,
String name,
String avatar,
Integer avatar,
String password,
String idCard,
Integer[] depIds);
@@ -77,7 +77,7 @@ public interface UserService extends IService<User> {
Map<Integer, List<Integer>> getDepIdsGroup(List<Integer> userIds);
void changeAvatar(Integer userId, String avatar);
void changeAvatar(Integer userId, Integer avatar);
void updateName(Integer id, String cn);

View File

@@ -24,6 +24,8 @@ import java.util.stream.Collectors;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import xyz.playedu.common.constant.BackendConstant;
import xyz.playedu.common.constant.CommonConstant;
import xyz.playedu.common.constant.ConfigConstant;
import xyz.playedu.common.domain.AppConfig;
import xyz.playedu.common.exception.ServiceException;
@@ -102,34 +104,26 @@ public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig
s3Config.setSecretKey(config.get(ConfigConstant.S3_SECRET_KEY));
s3Config.setBucket(config.get(ConfigConstant.S3_BUCKET));
s3Config.setEndpoint(config.get(ConfigConstant.S3_ENDPOINT));
s3Config.setDomain(config.get(ConfigConstant.S3_DOMAIN));
s3Config.setService(ConfigConstant.S3_SERVICE);
// region解析
String region = config.get(ConfigConstant.S3_REGION);
s3Config.setRegion(StringUtil.isEmpty(region) ? null : region);
if (StringUtil.isNotEmpty(s3Config.getService())
&& StringUtil.isNotEmpty(s3Config.getDomain())) {
String _domain = s3Config.getDomain();
// 拼接https://前缀
if (_domain.length() < 7
|| (!"http://".equalsIgnoreCase(_domain.substring(0, 7))
&& !"https://".equalsIgnoreCase(_domain.substring(0, 8)))) {
_domain = "https://" + _domain;
}
// 移除 / 后缀
if (StringUtil.endsWith(_domain, "/")) {
_domain = _domain.substring(0, _domain.length() - 1);
}
s3Config.setDomain(_domain);
}
return s3Config;
}
@Override
public List<Integer> getAllImageValue() {
return list(
query().getWrapper()
.eq("field_type", BackendConstant.APP_CONFIG_FIELD_TYPE_IMAGE)
.isNotNull("key_value"))
.stream()
.filter(appConfig -> !appConfig.getKeyValue().isEmpty())
.map(appConfig -> Integer.parseInt(appConfig.getKeyValue()))
.toList();
}
@Override
public boolean enabledLdapLogin() {
AppConfig appConfig =
@@ -138,10 +132,13 @@ public class AppConfigServiceImpl extends ServiceImpl<AppConfigMapper, AppConfig
}
@Override
public String defaultAvatar() {
public Integer defaultAvatar() {
AppConfig appConfig =
getOne(query().getWrapper().eq("key_name", ConfigConstant.MEMBER_DEFAULT_AVATAR));
return appConfig.getKeyValue();
if (StringUtil.isEmpty(appConfig.getKeyValue())) {
return CommonConstant.MINUS_ONE;
}
return Integer.parseInt(appConfig.getKeyValue());
}
@Override

View File

@@ -28,14 +28,14 @@ public class MemoryRateLimiterServiceImpl implements RateLimiterService {
public Long current(String key, Long duration) {
lock.lock();
try {
Long count = (Long) MemoryCacheUtil.get(key);
if (count == null) {
Object value = MemoryCacheUtil.get(key);
if (value == null) {
// 第一次访问,设置初始值和过期时间
MemoryCacheUtil.set(key, 1L, duration);
return 1L;
}
// 已存在计数器,直接自增
return MemoryCacheUtil.increment(key, 1L);
// 已存在计数器,直接自增increment方法已经能处理Long和AtomicLong类型
return MemoryCacheUtil.increment(key, 1L, duration);
} finally {
lock.unlock();
}

View File

@@ -90,7 +90,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
public User createWithDepIds(
String email,
String name,
String avatar,
Integer avatar,
String password,
String idCard,
Integer[] depIds) {
@@ -129,7 +129,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
User user,
String email,
String name,
String avatar,
Integer avatar,
String password,
String idCard,
Integer[] depIds) {
@@ -263,7 +263,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
}
@Override
public void changeAvatar(Integer userId, String avatar) {
public void changeAvatar(Integer userId, Integer avatar) {
User user = new User();
user.setId(userId);
user.setAvatar(avatar);

View File

@@ -30,5 +30,5 @@ public class UploadFileInfo {
private String saveName;
private String resourceType;
private String savePath;
private String url;
private String disk;
}

View File

@@ -22,8 +22,6 @@ public class S3Config {
private String accessKey;
private String secretKey;
private String bucket;
private String endpoint;
private String domain;
private String region;
private String service;
private String endpoint;
}

View File

@@ -145,13 +145,31 @@ public class MemoryCacheUtil {
}
// 内部方法
public static Long increment(String key, long delta) {
public static Long increment(String key, long delta, long expireSeconds) {
key = cacheNamePrefix + key;
CacheObject cacheObject = cache.get(key);
if (cacheObject == null || cacheObject.isExpired()) {
cache.put(key, new CacheObject(new AtomicLong(delta), Long.MAX_VALUE));
cache.put(
key,
new CacheObject(
new AtomicLong(delta),
System.currentTimeMillis() + expireSeconds * 1000));
return delta;
}
AtomicLong counter = (AtomicLong) cacheObject.getValue();
// 检查值的类型如果是Long类型将其转换为AtomicLong
Object value = cacheObject.getValue();
AtomicLong counter;
if (value instanceof Long) {
counter = new AtomicLong((Long) value);
cacheObject.setValue(counter); // 更新缓存对象中的值为AtomicLong类型
} else if (value instanceof AtomicLong) {
counter = (AtomicLong) value;
} else {
// 如果既不是Long也不是AtomicLong重新初始化为delta
counter = new AtomicLong(delta);
cacheObject.setValue(counter);
}
return counter.addAndGet(delta);
}

View File

@@ -23,6 +23,9 @@ import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.Grant;
import com.amazonaws.services.s3.model.GroupGrantee;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -67,9 +70,37 @@ public class S3Util {
AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard();
return builder.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(endpointConfiguration)
.build();
AmazonS3 client =
builder.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(endpointConfiguration)
.build();
// 检查bucket是否存在
if (client.doesBucketExistV2(defaultConfig.getBucket())) {
// 确保bucket为私有访问权限
AccessControlList acl = client.getBucketAcl(defaultConfig.getBucket());
boolean isPrivate = true;
// 检查是否有公开访问的权限
for (Grant grant : acl.getGrantsAsList()) {
if (grant.getGrantee() instanceof GroupGrantee
&& (GroupGrantee.AllUsers.equals(grant.getGrantee())
|| GroupGrantee.AuthenticatedUsers.equals(grant.getGrantee()))) {
isPrivate = false;
break;
}
}
if (!isPrivate) {
// 如果不是私有的,抛出异常
throw new ServiceException("Bucket " + defaultConfig.getBucket() + " 必须设置为私有访问权限");
}
} else {
// 如果bucket不存在抛出异常
throw new ServiceException("Bucket " + defaultConfig.getBucket() + " 不存在");
}
return client;
}
@SneakyThrows
@@ -100,6 +131,37 @@ public class S3Util {
return result.getUploadId();
}
@SneakyThrows
public UploadPartResult uploadPart(
byte[] file, String filename, String uploadId, int partNumber) {
InputStream inputStream = new ByteArrayInputStream(file);
UploadPartRequest uploadPartRequest =
new UploadPartRequest()
.withBucketName(defaultConfig.getBucket())
.withKey(filename)
.withUploadId(uploadId)
.withPartNumber(partNumber)
.withInputStream(inputStream)
.withPartSize(file.length);
// 上传分段文件
UploadPartResult uploadPartResult = getClient().uploadPart(uploadPartRequest);
return uploadPartResult;
}
public List<PartSummary> listParts(String uploadId, String filename) {
ListPartsRequest request =
new ListPartsRequest(defaultConfig.getBucket(), filename, uploadId);
PartListing partListing = getClient().listParts(request);
return partListing.getParts();
}
public void purgeSegments(String uploadId, String filename) {
AbortMultipartUploadRequest request =
new AbortMultipartUploadRequest(defaultConfig.getBucket(), filename, uploadId);
getClient().abortMultipartUpload(request);
}
public String generatePartUploadPreSignUrl(
String filename, String partNumber, String uploadId) {
GeneratePresignedUrlRequest request =
@@ -156,6 +218,21 @@ public class S3Util {
}
public String generateEndpointPreSignUrl(String path) {
return defaultConfig.getDomain() + "/" + path;
return generateEndpointPreSignUrl(path, "");
}
public String generateEndpointPreSignUrl(String path, String name) {
GeneratePresignedUrlRequest request =
new GeneratePresignedUrlRequest(defaultConfig.getBucket(), path, HttpMethod.GET);
request.setExpiration(new Date(System.currentTimeMillis() + 3600 * 3000)); // 三个小时有效期
// 文件名不为空
if (StringUtil.isNotEmpty(name)) {
ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
responseHeaders.setContentDisposition("attachment; filename=\"" + name + "\"");
request.setResponseHeaders(responseHeaders);
}
return getClient().generatePresignedUrl(request).toString();
}
}

View File

@@ -60,7 +60,7 @@
AND `users`.`name` LIKE concat('%',#{name},'%')
</if>
<if test="email != null and email != ''">
AND `users`.`email` = #{email}
AND `users`.`email` LIKE concat('%',#{email},'%')
</if>
<if test="idCard != null and idCard != ''">
AND `users`.`id_card` = #{idCard}
@@ -111,7 +111,7 @@
AND `users`.`name` LIKE concat('%',#{name},'%')
</if>
<if test="email != null and email != ''">
AND `users`.`email` = #{email}
AND `users`.`email` LIKE concat('%',#{email},'%')
</if>
<if test="idCard != null and idCard != ''">
AND `users`.`id_card` = #{idCard}