推荐算法 用户考核 头像上传
Change-Id: Iaac96768d5238142f5ed445f5cc64ccedd239d0f
diff --git a/src/main/java/com/pt5/pthouduan/service/ExamService.java b/src/main/java/com/pt5/pthouduan/service/ExamService.java
new file mode 100644
index 0000000..93dbbcd
--- /dev/null
+++ b/src/main/java/com/pt5/pthouduan/service/ExamService.java
@@ -0,0 +1,103 @@
+package com.pt5.pthouduan.service;
+
+import com.pt5.pthouduan.entity.Invites;
+import com.pt5.pthouduan.entity.User;
+import com.pt5.pthouduan.entity.UserTrafficStat;
+import com.pt5.pthouduan.mapper.InvitesMapper;
+import com.pt5.pthouduan.mapper.UserMapper;
+import com.pt5.pthouduan.mapper.UserTrafficMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class ExamService {
+ @Autowired
+ private UserTrafficMapper userTrafficMapper;
+ @Autowired
+ private UserMapper userMapper;
+ //所有用户月度下载量考核
+ public Map<String, Object> MonthDownload(LocalDate startDate,LocalDate endDate){
+ Map<String, Object> result = new HashMap<>();
+ List<Map<String, Object>> users = userMapper.selectAllUsersBasicInfo();
+ for (Map<String, Object> user : users) {
+ // 获取 gradeId,这里转为数字
+ Object gradeIdObj = user.get("grade_id");
+ int gradeId = (gradeIdObj instanceof Number) ? ((Number) gradeIdObj).intValue() : 0;
+ UserTrafficStat userTrafficStat=userTrafficMapper.getUserTrafficStats((String) user.get("passkey"),startDate,endDate);
+ System.out.println(gradeId+" "+userTrafficStat.getTotalUploaded()+" "+userTrafficStat.getTotalDownloaded());
+ // 根据 gradeId 的值审核下载量
+ if (gradeId == 1) {
+ if(userTrafficStat.getTotalDownloaded()<1073741824){
+ if(userTrafficStat.getTotalUploaded()< 1073741824L *50)
+ failure((String) user.get("username"),gradeId);
+ }
+ } else if (gradeId == 2) {
+ if(userTrafficStat.getTotalDownloaded()< 1073741824L *3){
+ if(userTrafficStat.getTotalUploaded()< 1073741824L *50)
+ failure((String) user.get("username"),gradeId);
+ }
+ } else if (gradeId == 3) {
+ if(userTrafficStat.getTotalDownloaded()< 1073741824L *5){
+ if(userTrafficStat.getTotalUploaded()< 1073741824L *50)
+ failure((String) user.get("username"),gradeId);
+ }
+ }
+ }
+ result.put("success", true);
+ result.put("message", "用户月度考核完毕");
+ return result;
+ }
+ //考核失败
+ void failure(String username,int gradeId){
+ System.out.println("failure"+username+gradeId);
+ if(gradeId == 1){
+ userMapper.deleteByUsername(username);
+ }else{
+ userMapper.updateGrade(username,gradeId-1);
+ }
+ }
+ //所有用户季度上传量考核
+ public Map<String, Object> QuarterUpload(LocalDate startDate,LocalDate endDate){
+ Map<String, Object> result = new HashMap<>();
+ List<Map<String, Object>> users = userMapper.selectAllUsersBasicInfo();
+ for (Map<String, Object> user : users) {
+ // 获取 gradeId,这里转为数字
+ Object gradeIdObj = user.get("grade_id");
+ int gradeId = (gradeIdObj instanceof Number) ? ((Number) gradeIdObj).intValue() : 0;
+ UserTrafficStat userTrafficStat=userTrafficMapper.getUserTrafficStats((String) user.get("passkey"),startDate,endDate);
+ // 根据 gradeId 的值审核上传
+ if (gradeId == 1) {
+ if(userTrafficStat.getTotalUploaded()< 1073741824L *50){
+ failure((String) user.get("username"),gradeId);
+ }
+ } else if (gradeId == 2) {
+ if(userTrafficStat.getTotalUploaded()< 1073741824L*60){
+ failure((String) user.get("username"),gradeId);
+ }
+ } else if (gradeId == 3) {
+ System.out.println("here");
+ if(userTrafficStat.getTotalUploaded()< 1073741824L*70){
+ System.out.println("failure");
+ failure((String) user.get("username"),gradeId);
+ }
+ }else if (gradeId == 4) {
+ if(userTrafficStat.getTotalUploaded()< 1073741824L*80){
+ failure((String) user.get("username"),gradeId);
+ }
+ }else if (gradeId == 5) {
+ if(userTrafficStat.getTotalUploaded()< 1073741824L*100){
+ failure((String) user.get("username"),gradeId);
+ }
+ }
+ }
+ result.put("success", true);
+ result.put("message", "用户季度考核完毕");
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pt5/pthouduan/service/RecommendService.java b/src/main/java/com/pt5/pthouduan/service/RecommendService.java
new file mode 100644
index 0000000..6f6f9a4
--- /dev/null
+++ b/src/main/java/com/pt5/pthouduan/service/RecommendService.java
@@ -0,0 +1,170 @@
+package com.pt5.pthouduan.service;
+
+import com.pt5.pthouduan.entity.Torrent;
+import com.pt5.pthouduan.entity.UserBehavior;
+import com.pt5.pthouduan.mapper.RecommendMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+@Service
+public class RecommendService {
+ @Autowired
+ private TorrentService torrentService;
+
+ @Autowired
+ private RecommendMapper recommendMapper;
+ //收集用户行为数据
+ public void recordUserBehavior(Long userId, Long torrentId, String behaviorType) {
+ UserBehavior behavior = new UserBehavior();
+ behavior.setUserId(userId);
+ behavior.setTorrentId(torrentId);
+ behavior.setBehaviorType(behaviorType);
+ behavior.setWeight(getBehaviorWeight(behaviorType));
+ behavior.setTimestamp(LocalDateTime.now());
+
+ recommendMapper.insertUserBehavior(behavior);
+ }
+ //对用户行为数据进行定期清洗 维护内存空间
+ private void cleanOldBehaviors() {
+ recommendMapper.deleteOldBehaviorsForAllUsers(30);
+ }
+
+ //设定不同用户行为的偏好权重(注意这里权重如果修改 那么BehaviorType同样需要修改 它在entity中)
+ private int getBehaviorWeight(String behaviorType) {
+ return switch (behaviorType) {
+ case "DOWNLOAD" -> 3;
+ case "FAVORITE" -> 5;
+ case "SEED" -> 4;
+ case "VIEW" -> 1;
+ case "COMMENT" -> 2;
+ default -> 1;
+ };
+ }
+
+
+
+ public List<Torrent> recommendForUser(Long userId) {//limit为资源的最大数量限制
+ // 检查用户是否有足够的行为数据
+ double totalWeight=recommendMapper.sumWeightsByUserId(userId);
+
+ if (totalWeight>50) {//行为数据足够
+ // 70% 偏好资源,15% 热门资源,15% 最新资源
+ return getPersonalizedRecommendation(userId);
+ } else {//行为数据不足
+ // 50% 热门资源,50% 最新资源
+ return getDefaultRecommendation();
+ }
+ }
+
+ //偏好资源推荐
+ private List<Torrent> getPersonalizedRecommendation(Long userId) {
+
+ // 获取偏好资源
+
+ List<Map<String, Object>> categories = recommendMapper.findFavoriteCategories(userId);//获取喜爱的类型及其权重
+ categories = categories.stream()
+ .sorted((a, b) -> Integer.compare(
+ ((Number)b.get("totalWeight")).intValue(),
+ ((Number)a.get("totalWeight")).intValue()
+ ))
+ .limit(5) // 只取前5种偏好类型
+ .toList();
+ List<List<Torrent>> allTorrents = categories.stream()
+ .map(category -> torrentService.getTorrentsByCategory((Integer) category.get("categoryId")))
+ .toList();
+ // 3. 均匀混合算法
+ List<Torrent> combinedList = new ArrayList<>();
+ if (!allTorrents.isEmpty()) {
+ int maxSize = allTorrents.stream()
+ .mapToInt(List::size)
+ .max()
+ .orElse(0);
+
+ // 按权重计算每种类型每次应该添加的元素数量
+ int[] weights = categories.stream()
+ .mapToInt(c -> ((Number)c.get("totalWeight")).intValue()) // 安全转换
+ .toArray();
+ int totalWeight = Arrays.stream(weights).sum();
+
+ // 计算每种类型在每轮中应该添加的比例
+ double[] ratios = Arrays.stream(weights)
+ .mapToDouble(w -> (double)w / totalWeight)
+ .toArray();
+
+ // 均匀混合实现
+ for (int i = 0; i < maxSize; i++) {
+ for (int j = 0; j < allTorrents.size(); j++) {
+ List<Torrent> current = allTorrents.get(j);
+ if (i < current.size()) {
+ // 根据权重比例决定是否添加当前元素
+ if (Math.random() < ratios[j]) {
+ combinedList.add(current.get(i));
+ }
+ }
+ }
+ }
+ }
+
+ // 常规推荐
+ List<Torrent> regular=getDefaultRecommendation();
+ //合并结果 把个性化推荐和普通推荐相结合7:3
+ List<Torrent> finalList = new ArrayList<>();
+ int combinedSize = combinedList.size();
+ int regularSize = regular.size();
+ int actualCombined = combinedSize;
+ int actualRegular =regularSize;
+ Iterator<Torrent> combinedIter = combinedList.iterator();
+ Iterator<Torrent> regularIter = regular.iterator();
+ while (actualCombined > 0 || actualRegular > 0) {
+ double ratio = 0.9;
+
+ if (Math.random() < ratio && actualCombined > 0) {
+ if (combinedIter.hasNext()) {
+ finalList.add(combinedIter.next());
+ actualCombined--;
+ }
+ } else if (actualRegular > 0) {
+ if (regularIter.hasNext()) {
+ finalList.add(regularIter.next());
+ actualRegular--;
+ }
+ }
+ }
+ while (regularIter.hasNext()) {
+ finalList.add(regularIter.next());
+ }
+ Set<Integer> seenIds = new HashSet<>();
+ List<Torrent> distinctList = finalList.stream()
+ .filter(torrent -> seenIds.add(torrent.getTorrentid().intValue())) // 首次出现时返回true
+ .collect(Collectors.toList());
+ return distinctList;
+ }
+ //无偏好推荐
+ private List<Torrent> getDefaultRecommendation() {
+ List<Torrent> torrents=torrentService.getAllTorrents();
+ torrents= torrents.stream()
+ .sorted((t1, t2) -> {
+ // 时间衰减因子(新种子加分)
+ long daysOld1 = ChronoUnit.DAYS.between(t1.getUploadTime(), LocalDateTime.now());
+ long daysOld2 = ChronoUnit.DAYS.between(t2.getUploadTime(), LocalDateTime.now());
+
+ // 综合分数 = downloadCount + (1 / (daysOld + 1)) * 系数
+ double score1 = t1.getDownloadCount() + 10.0 / (daysOld1 + 1);
+ double score2 = t2.getDownloadCount() + 10.0 / (daysOld2 + 1);
+
+ return Double.compare(score2, score1);
+ })
+ .toList();
+ return torrents;
+ }
+
+// public List<Map<String, Object>> test(){
+// return recommendMapper.findFavoriteCategories(1L);
+// }
+}