feat(backend): Improve authorization governance and video interactions

This commit is contained in:
2026-04-24 17:34:09 +08:00
parent 6c954542ac
commit 600ff66bd3
42 changed files with 1239 additions and 74 deletions
@@ -33,12 +33,13 @@ public class AdminUserController {
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "status", required = false) Integer status,
HttpServletRequest request) {
if (!isInternalRequest(request)) {
return Result.error("非法的内部调用");
}
Page<UserInfo> pageParam = new Page<>(page, size);
return userInfoService.getAdminUserList(pageParam, keyword);
return userInfoService.getAdminUserList(pageParam, keyword, status);
}
/**
@@ -172,6 +172,34 @@ public class UserProfileController {
return userInfoService.increaseFavoriteCount(userId, count);
}
/**
* 增加用户硬币余额
*/
@PostMapping("/count/coin/increase/{userId}/{count}")
public Result<Void> increaseCoinCount(
@PathVariable Long userId,
@PathVariable Integer count,
HttpServletRequest request) {
if (!isInternalRequest(request)) {
return Result.error("非法的内部调用");
}
return userInfoService.increaseCoinCount(userId, count);
}
/**
* 扣减用户硬币余额
*/
@PostMapping("/count/coin/decrease/{userId}/{count}")
public Result<Void> decreaseCoinCount(
@PathVariable Long userId,
@PathVariable Integer count,
HttpServletRequest request) {
if (!isInternalRequest(request)) {
return Result.error("非法的内部调用");
}
return userInfoService.decreaseCoinCount(userId, count);
}
/**
* 从请求中获取用户名
*/
@@ -4,6 +4,8 @@ import com.nuaa.video.common.model.vo.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 认证服务Feign客户端
*/
@@ -44,4 +46,11 @@ public interface AuthFeignClient {
@PathVariable Long userId,
@RequestParam String newPassword,
@RequestHeader("X-Internal-Token") String internalToken);
/**
* 按账号状态查询用户ID - 管理接口
*/
@GetMapping("/admin/user-ids/status/{status}")
Result<List<Long>> getUserIdsByStatus(@PathVariable Integer status,
@RequestHeader("X-Internal-Token") String internalToken);
}
@@ -43,4 +43,14 @@ public interface UserInfoMapper extends BaseMapper<UserInfo> {
* 增加收藏数
*/
int increaseFavoriteCount(@Param("userId") Long userId, @Param("count") int count);
/**
* 增加硬币余额
*/
int increaseCoinCount(@Param("userId") Long userId, @Param("count") int count);
/**
* 扣减硬币余额
*/
int decreaseCoinCount(@Param("userId") Long userId, @Param("count") int count);
}
@@ -93,10 +93,20 @@ public interface UserInfoService extends IService<UserInfo> {
*/
Result<Void> increaseFavoriteCount(Long userId, Integer count);
/**
* 增加用户硬币余额
*/
Result<Void> increaseCoinCount(Long userId, Integer count);
/**
* 扣减用户硬币余额
*/
Result<Void> decreaseCoinCount(Long userId, Integer count);
/**
* 获取用户列表 - 管理接口
*/
Result<Page<UserProfileVO>> getAdminUserList(Page<UserInfo> page, String keyword);
Result<Page<UserProfileVO>> getAdminUserList(Page<UserInfo> page, String keyword, Integer status);
/**
* 获取用户详情 - 管理接口
@@ -27,6 +27,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -41,9 +43,15 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
private static final ZoneId APP_ZONE_ID = ZoneId.of("Asia/Shanghai");
private static final long DEFAULT_AVATAR_MAX_SIZE_BYTES = 5L * 1024 * 1024;
@Value("${service.auth.token}")
private String internalServiceToken;
@Value("${user.avatar.max-size-bytes:" + DEFAULT_AVATAR_MAX_SIZE_BYTES + "}")
private long avatarMaxSizeBytes;
private final UserInfoMapper userInfoMapper;
private final UserFollowMapper userFollowMapper;
private final UserSettingService userSettingService;
@@ -118,6 +126,19 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
}
if (file == null || file.isEmpty()) {
return Result.error("请选择头像文件");
}
String contentType = file.getContentType();
if (!StringUtils.hasText(contentType) || !contentType.startsWith("image/")) {
return Result.error("仅支持上传图片格式头像");
}
if (file.getSize() > avatarMaxSizeBytes) {
return Result.error("头像文件不能超过5MB");
}
// 上传头像
String avatarUrl = minioUtils.uploadImage(file);
@@ -127,8 +148,8 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
userInfo.setUpdateTime(LocalDateTime.now());
updateById(userInfo);
// 返回头像URL
return Result.success(minioUtils.getPreSignedUrl(avatarUrl));
// 返回对象路径,由前端统一拼接可访问的媒体地址
return Result.success(avatarUrl);
} catch (Exception e) {
log.error("上传头像失败", e);
return Result.error("上传头像失败: " + e.getMessage());
@@ -154,7 +175,7 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
userInfo.setFollowingCount(0);
userInfo.setVideoCount(0);
userInfo.setLikeCount(0);
userInfo.setCoinCount(0);
userInfo.setCoinCount(20);
userInfo.setFavoriteCount(0);
userInfo.setViewCount(0L);
userInfo.setLevel(1);
@@ -247,14 +268,64 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
}
@Override
public Result<Page<UserProfileVO>> getAdminUserList(Page<UserInfo> page, String keyword) {
public Result<Void> increaseCoinCount(Long userId, Integer count) {
if (count == null || count <= 0) {
return Result.error("硬币数量无效");
}
UserInfo userInfo = getById(userId);
if (userInfo == null) {
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
}
userInfoMapper.increaseCoinCount(userId, count);
updateUserStatDailyAsync(userId, "coin", count);
return Result.success();
}
@Override
public Result<Void> decreaseCoinCount(Long userId, Integer count) {
if (count == null || count <= 0) {
return Result.error("硬币数量无效");
}
UserInfo userInfo = getById(userId);
if (userInfo == null) {
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
}
int affectedRows = userInfoMapper.decreaseCoinCount(userId, count);
if (affectedRows <= 0) {
return Result.error("硬币余额不足");
}
updateUserStatDailyAsync(userId, "coin", -count);
return Result.success();
}
@Override
public Result<Page<UserProfileVO>> getAdminUserList(Page<UserInfo> page, String keyword, Integer status) {
LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
// 添加关键词搜索条件
if (StringUtils.hasText(keyword)) {
queryWrapper.like(UserInfo::getNickname, keyword)
queryWrapper.and(wrapper -> wrapper
.like(UserInfo::getNickname, keyword)
.or()
.like(UserInfo::getSignature, keyword);
.like(UserInfo::getSignature, keyword));
}
if (status != null) {
Result<List<Long>> userIdsResult = authFeignClient.getUserIdsByStatus(status, internalServiceToken);
List<Long> userIds = userIdsResult.getData() == null ? Collections.emptyList() : userIdsResult.getData();
if (userIds.isEmpty()) {
Page<UserProfileVO> emptyPage = new Page<>(page.getCurrent(), page.getSize(), 0);
emptyPage.setRecords(Collections.emptyList());
return Result.success(emptyPage);
}
queryWrapper.in(UserInfo::getId, userIds);
}
// 按创建时间降序排序
@@ -346,7 +417,7 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> i
stats.put("totalUsers", totalUsers);
// 今日新增用户
LocalDate today = LocalDate.now();
LocalDate today = LocalDate.now(APP_ZONE_ID);
long todayNewUsers = count(new LambdaQueryWrapper<UserInfo>()
.ge(UserInfo::getCreateTime, today.atStartOfDay()));
stats.put("todayNewUsers", todayNewUsers);
@@ -68,6 +68,11 @@ public class UserProfileVO implements Serializable {
*/
private Integer likeCount;
/**
* 硬币余额
*/
private Integer coinCount;
/**
* 等级
*/
@@ -92,4 +97,4 @@ public class UserProfileVO implements Serializable {
* 创建时间
*/
private LocalDateTime createTime;
}
}
@@ -37,4 +37,19 @@
WHERE id = #{userId}
</update>
<update id="increaseCoinCount">
UPDATE user_info
SET coin_count = GREATEST(coin_count + #{count}, 0),
update_time = NOW()
WHERE id = #{userId}
</update>
<update id="decreaseCoinCount">
UPDATE user_info
SET coin_count = coin_count - #{count},
update_time = NOW()
WHERE id = #{userId}
AND coin_count >= #{count}
</update>
</mapper>