Просмотр исходного кода

chore: auto bump version and changelog [skip ci]

iwt 1 месяц назад
Родитель
Сommit
3afd3cd0af

+ 9 - 0
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -8304,5 +8304,14 @@ private void runMigration100() {
 
 		// 迁移194: family_join_requests 添加 family_role 列(保存申请人角色)
 		ensureColumn("family_join_requests", "family_role", "VARCHAR(20) DEFAULT NULL COMMENT '申请人在家庭中的角色:爸爸/妈妈/爷爷/奶奶等'");
+
+		// 迁移195: tasks 表新增 accept_deadline 列 + 扩展 status 枚举(支持 assigned/in_progress/rejected)
+		ensureColumn("tasks", "accept_deadline", "DATETIME DEFAULT NULL COMMENT '接受截止时间(分配给其他家庭成员的任务)'");
+		try {
+			jdbcTemplate.execute("ALTER TABLE tasks MODIFY COLUMN `status` ENUM('pending','assigned','in_progress','completed','rejected','overdue','cancelled') DEFAULT 'pending' COMMENT '状态: pending=待审核 assigned=待接受 in_progress=进行中 completed=已完成 rejected=已拒绝 overdue=已过期 cancelled=已取消'");
+			log.info("已更新tasks.status枚举值");
+		} catch (Exception e) {
+			log.warn("tasks.status枚举修改失败(可能已修改): " + e.getMessage());
+		}
 	}
 }

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/Task.java

@@ -89,6 +89,9 @@ private Date earliestStart;
 // 最晚结束时间
 private Date latestEnd;
 
+/** 接受截止时间(分配给其他家庭成员的任务使用) */
+private Date acceptDeadline;
+
     // 小游戏代码(当category为'小游戏类'时使用): schulte/1a2b/sudoku
     private String minigameCode;
 

+ 112 - 4
cfc-backend/src/main/java/com/etotem/cfc/service/TaskService.java

@@ -48,6 +48,9 @@ public class TaskService implements TaskServiceInterface {
     @Resource
     private OnboardingService onboardingService;
 
+    @Resource
+    private FamilyRelationshipScoreMapper familyRelationshipScoreMapper;
+
     @Resource
     private GrowthTaskService growthTaskService;
 
@@ -59,10 +62,9 @@ public class TaskService implements TaskServiceInterface {
     public Long createTask(Long userId, CreateTaskDTO dto) {
         User currentUser = userService.getUserInfo(userId);
         
-        // 处理执行者类型 - 支持孩子任务和家长任务
+        // 处理执行者类型 - 支持孩子任务、家长任务、家庭成员任务
         String executorType = dto.getExecutorType();
         if (executorType == null || executorType.isEmpty()) {
-            // 兼容旧版:默认给孩子创建任务
             executorType = "child";
         }
         
@@ -109,11 +111,14 @@ public class TaskService implements TaskServiceInterface {
         task.setExecutorType(executorType);
         
         if ("child".equals(executorType)) {
-            // 给孩子安排的任务
             task.setExecutorId(memberId);
             task.setChildId(memberId);
+        } else if ("member".equals(executorType)) {
+            task.setExecutorId(memberId);
+            task.setChildId(null);
+            task.setStatus("assigned");
+            task.setAcceptDeadline(dto.getDeadline());
         } else {
-            // 给家长安排的任务
             task.setExecutorId(dto.getExecutorId() != null ? dto.getExecutorId() : userId);
             task.setChildId(null);
         }
@@ -893,4 +898,107 @@ return 0; // 达到每日扣分上限
         wrapper.orderByDesc(Task::getCreatedAt);
         return taskMapper.selectPage(new Page<>(page, size), wrapper);
     }
+
+    /**
+     * 获取当前用户收到的待接受任务列表(executor_type='member' AND status='assigned')
+     */
+    public List<Task> getMyPendingTasks(Long memberId) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        Date startOfDay = calendar.getTime();
+
+        calendar.add(Calendar.DAY_OF_MONTH, 1);
+        Date endOfDay = calendar.getTime();
+
+        return taskMapper.selectList(new LambdaQueryWrapper<Task>()
+                .eq(Task::getExecutorType, "member")
+                .eq(Task::getExecutorId, memberId)
+                .eq(Task::getStatus, "assigned")
+                .ge(Task::getDeadline, startOfDay)
+                .lt(Task::getDeadline, endOfDay)
+                .or()
+                .and(w -> w.eq(Task::getExecutorType, "member")
+                        .eq(Task::getExecutorId, memberId)
+                        .eq(Task::getStatus, "assigned")
+                        .isNull(Task::getDeadline))
+                .orderByAsc(Task::getDeadline));
+    }
+
+    /**
+     * 接受任务:status 从 assigned 改为 in_progress
+     */
+    @Transactional
+    public boolean acceptTask(Long taskId, Long memberId) {
+        Task task = taskMapper.selectById(taskId);
+        if (task == null || !task.getExecutorId().equals(memberId) || !"assigned".equals(task.getStatus())) {
+            return false;
+        }
+        task.setStatus("in_progress");
+        task.setAcceptDeadline(null);
+        task.setUpdatedAt(new Date());
+        taskMapper.updateById(task);
+        return true;
+    }
+
+    /**
+     * 拒绝任务:status 改为 rejected,调整 creator→assignee 和 assignee→creator 的关系分数
+     */
+    @Transactional
+    public boolean rejectTask(Long taskId, Long memberId) {
+        Task task = taskMapper.selectById(taskId);
+        if (task == null || !task.getExecutorId().equals(memberId) || !"assigned".equals(task.getStatus())) {
+            return false;
+        }
+        task.setStatus("rejected");
+        task.setUpdatedAt(new Date());
+        taskMapper.updateById(task);
+
+        Long creatorId = task.getCreatorId();
+        Long assigneeId = memberId;
+        Long familyId = task.getFamilyId();
+        if (creatorId == null || assigneeId == null || familyId == null) return true;
+
+        int trustDelta = -1;
+        int intimacyDelta = -1;
+        int commDelta = -1;
+
+        adjustRelationshipScore(familyId, creatorId, assigneeId, trustDelta, intimacyDelta, commDelta);
+        adjustRelationshipScore(familyId, assigneeId, creatorId, 0, 0, 1);
+        return true;
+    }
+
+    private void adjustRelationshipScore(Long familyId, Long fromId, Long toId, int trustDelta, int intimacyDelta, int commDelta) {
+        FamilyRelationshipScore score = familyRelationshipScoreMapper.selectOne(
+                new LambdaQueryWrapper<FamilyRelationshipScore>()
+                        .eq(FamilyRelationshipScore::getFamilyId, familyId)
+                        .eq(FamilyRelationshipScore::getFromMemberId, fromId)
+                        .eq(FamilyRelationshipScore::getToMemberId, toId));
+        if (score == null) {
+            FamilyMember from = familyMemberMapper.selectById(fromId);
+            score = new FamilyRelationshipScore();
+            score.setFamilyId(familyId);
+            score.setFromMemberId(fromId);
+            score.setToMemberId(toId);
+            score.setTrustScore(from != null && from.getTrustScore() != null ? from.getTrustScore() : new java.math.BigDecimal("60.00"));
+            score.setIntimacyScore(from != null && from.getIntimacyScore() != null ? from.getIntimacyScore() : new java.math.BigDecimal("60.00"));
+            score.setCommunicationScore(from != null && from.getCommunicationScore() != null ? from.getCommunicationScore() : new java.math.BigDecimal("60.00"));
+            try {
+                familyRelationshipScoreMapper.insert(score);
+            } catch (Exception e) {
+                score = familyRelationshipScoreMapper.selectOne(
+                        new LambdaQueryWrapper<FamilyRelationshipScore>()
+                                .eq(FamilyRelationshipScore::getFamilyId, familyId)
+                                .eq(FamilyRelationshipScore::getFromMemberId, fromId)
+                                .eq(FamilyRelationshipScore::getToMemberId, toId));
+            }
+        }
+        if (score == null) return;
+        if (trustDelta != 0) score.setTrustScore(score.getTrustScore().add(java.math.BigDecimal.valueOf(trustDelta)));
+        if (intimacyDelta != 0) score.setIntimacyScore(score.getIntimacyScore().add(java.math.BigDecimal.valueOf(intimacyDelta)));
+        if (commDelta != 0) score.setCommunicationScore(score.getCommunicationScore().add(java.math.BigDecimal.valueOf(commDelta)));
+        familyRelationshipScoreMapper.updateById(score);
+    }
 }

+ 5 - 3
cfc-backend/src/main/resources/schema.sql

@@ -69,17 +69,18 @@ CREATE TABLE IF NOT EXISTS tasks (
     family_id BIGINT NOT NULL,
     creator_id BIGINT NOT NULL,
     child_id BIGINT COMMENT '执行者ID(child任务时必填,parent任务时可空)',
-    executor_type VARCHAR(20) COMMENT '执行者类型: child/parent',
+    executor_type VARCHAR(20) COMMENT '执行者类型: child/parent/member',
     executor_id BIGINT COMMENT '执行者ID',
     title VARCHAR(200) NOT NULL,
     description TEXT,
     points TINYINT DEFAULT 2 COMMENT '任务积分1-10',
     deadline DATETIME NOT NULL,
+    accept_deadline DATETIME DEFAULT NULL COMMENT '接受截止时间(分配给其他家庭成员的任务)',
     repeat_type ENUM('none', 'daily', 'weekly') DEFAULT 'none',
     category VARCHAR(32),
     need_review TINYINT DEFAULT 0 COMMENT '是否需要审核',
     review_by_category TINYINT DEFAULT 0 COMMENT '是否按分类审核',
-    status ENUM('pending', 'completed', 'overdue', 'cancelled') DEFAULT 'pending',
+    status ENUM('pending', 'assigned', 'in_progress', 'completed', 'rejected', 'overdue', 'cancelled') DEFAULT 'pending',
     completed_at DATETIME,
     bonus_rule JSON COMMENT '积分奖励规则',
     points_expire_days INT DEFAULT 0 COMMENT '积分有效期天数',
@@ -91,7 +92,8 @@ CREATE TABLE IF NOT EXISTS tasks (
     INDEX idx_status (status),
     INDEX idx_category (category),
     INDEX idx_executor (executor_type, executor_id),
-    INDEX idx_dimension_code (dimension_code)
+    INDEX idx_dimension_code (dimension_code),
+    INDEX idx_executor_assigned (executor_type, executor_id, status)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
 -- 积分流水表

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-5d433f5b5282ff51bd22fcbe10bfa6becfe75cef
+ac0cdfc09af1368cab08fb1a5a7075d3b020978d

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "cfc-web",
-  "version": "1.0.882",
+  "version": "1.0.883",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "cfc-web",
-      "version": "1.0.882",
+      "version": "1.0.883",
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
   "name": "cfc-web",
-  "version": "1.0.883",
+  "version": "1.0.884",
   "private": true,
   "scripts": {
     "dev": "vue-cli-service serve",