Browse Source

feat(challenge): 管理端挑战评分配置页+AdminChallengeScoreConfigController

asus 1 month ago
parent
commit
34bdceee62

+ 56 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminChallengeScoreConfigController.java

@@ -0,0 +1,56 @@
+package com.etotem.cfc.controller.admin;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.entity.FamilyChallengeScoreConfig;
+import com.etotem.cfc.mapper.FamilyChallengeScoreConfigMapper;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+
+@Tag(name = "管理端-挑战评分配置")
+@RestController
+@RequestMapping("/api/admin/challenge-score-config")
+public class AdminChallengeScoreConfigController {
+
+    @Resource
+    private FamilyChallengeScoreConfigMapper configMapper;
+
+    @Operation(summary = "获取全部挑战评分配置")
+    @PostMapping("/list")
+    public Result<List<FamilyChallengeScoreConfig>> list() {
+        List<FamilyChallengeScoreConfig> list = configMapper.selectList(
+                new LambdaQueryWrapper<FamilyChallengeScoreConfig>()
+                        .orderByAsc(FamilyChallengeScoreConfig::getAction)
+                        .orderByAsc(FamilyChallengeScoreConfig::getScoreType));
+        return Result.success(list);
+    }
+
+    @Operation(summary = "更新挑战评分配置(按 action+scoreType)")
+    @PostMapping("/update")
+    public Result<String> update(@RequestBody FamilyChallengeScoreConfig config) {
+        if (config.getAction() == null || config.getScoreType() == null) {
+            return Result.error("action和scoreType不能为空");
+        }
+        FamilyChallengeScoreConfig existing = configMapper.selectOne(
+                new LambdaQueryWrapper<FamilyChallengeScoreConfig>()
+                        .eq(FamilyChallengeScoreConfig::getAction, config.getAction())
+                        .eq(FamilyChallengeScoreConfig::getScoreType, config.getScoreType()));
+        if (existing == null) {
+            // 不存在则按默认创建
+            config.setEnabled(config.getEnabled() != null ? config.getEnabled() : 1);
+            config.setUpdatedAt(new Date());
+            configMapper.insert(config);
+            return Result.success("已创建配置");
+        }
+        if (config.getValue() != null) existing.setValue(config.getValue());
+        if (config.getEnabled() != null) existing.setEnabled(config.getEnabled());
+        existing.setUpdatedAt(new Date());
+        configMapper.updateById(existing);
+        return Result.success("更新成功");
+    }
+}

+ 9 - 0
cfc-web/src/api/challengeScore.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getChallengeScoreConfigList() {
+  return request({ url: '/api/admin/challenge-score-config/list', method: 'post' })
+}
+
+export function updateChallengeScoreConfig(data) {
+  return request({ url: '/api/admin/challenge-score-config/update', method: 'post', data })
+}

+ 6 - 0
cfc-web/src/router/index.js

@@ -311,6 +311,12 @@ const routes = [
       name: 'EnergyBehaviorConfig',
       component: () => import('@/views/admin/EnergyBehaviorConfig.vue'),
       meta: { title: '能量行为配置', perm: 'energy' }
+    },
+    {
+      path: 'challenge-score-config',
+      name: 'ChallengeScoreConfig',
+      component: () => import('@/views/admin/ChallengeScoreConfig.vue'),
+      meta: { title: '挑战评分配置', perm: 'energy' }
     },
       // ========== 文章管理 ==========
       {

+ 113 - 0
cfc-web/src/views/admin/ChallengeScoreConfig.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="challenge-score-config admin-page">
+    <div class="header">
+      <h2>挑战评分配置</h2>
+      <div class="header-tip">家庭挑战 发起/同意/拒绝 的方向性关系评分与能量增减(可正可负)</div>
+    </div>
+
+    <el-table :data="matrix" v-loading="loading" border stripe>
+      <el-table-column label="动作" width="120">
+        <template slot-scope="{ row }">
+          <el-tag size="mini" :type="row.action === 'reject' ? 'danger' : 'success'">{{ actionLabel(row.action) }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="能量(行维度)" width="140">
+        <template slot-scope="{ row }">
+          <el-input-number v-model="row.energy" :min="-10" :max="10" size="mini" />
+        </template>
+      </el-table-column>
+      <el-table-column label="信任(方向)" width="140">
+        <template slot-scope="{ row }">
+          <el-input-number v-model="row.trust" :min="-10" :max="10" size="mini" />
+        </template>
+      </el-table-column>
+      <el-table-column label="亲近(方向)" width="140">
+        <template slot-scope="{ row }">
+          <el-input-number v-model="row.intimacy" :min="-10" :max="10" size="mini" />
+        </template>
+      </el-table-column>
+      <el-table-column label="沟通(方向)" width="140">
+        <template slot-scope="{ row }">
+          <el-input-number v-model="row.communication" :min="-10" :max="10" size="mini" />
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="120" fixed="right">
+        <template slot-scope="{ row }">
+          <el-button size="mini" type="primary" @click="saveRow(row)">保存</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="footer-tip">注:reject 的沟通方向由代码固定(拒绝人→发起人 +、发起人→拒绝人 -、已同意成员→拒绝人 -),此处的值为增减幅度。信任/亲近为单向扣减(发起人→拒绝人)。</div>
+  </div>
+</template>
+
+<script>
+import { getChallengeScoreConfigList, updateChallengeScoreConfig } from '@/api/challengeScore'
+
+export default {
+  name: 'ChallengeScoreConfig',
+  data() {
+    return {
+      loading: false,
+      matrix: []
+    }
+  },
+  created() {
+    this.loadList()
+  },
+  methods: {
+    actionLabel: function(action) {
+      var map = { initiate: '发起', agree: '同意', reject: '拒绝' }
+      return map[action] || action
+    },
+    loadList: function() {
+      var self = this
+      self.loading = true
+      getChallengeScoreConfigList().then(function(res) {
+        var list = (res && res.data) || []
+        var matrix = []
+        var actions = ['initiate', 'agree', 'reject']
+        for (var i = 0; i < actions.length; i++) {
+          var row = { action: actions[i], energy: 0, trust: 0, intimacy: 0, communication: 0 }
+          for (var j = 0; j < list.length; j++) {
+            var item = list[j]
+            if (item.action !== row.action) continue
+            if (item.scoreType === 'energy') row.energy = item.value
+            if (item.scoreType === 'trust') row.trust = item.value
+            if (item.scoreType === 'intimacy') row.intimacy = item.value
+            if (item.scoreType === 'communication') row.communication = item.value
+          }
+          matrix.push(row)
+        }
+        self.matrix = matrix
+      }).catch(function() {
+        self.matrix = []
+      }).finally(function() {
+        self.loading = false
+      })
+    },
+    saveRow: function(row) {
+      var self = this
+      var items = [
+        { action: row.action, scoreType: 'energy', value: row.energy },
+        { action: row.action, scoreType: 'trust', value: row.trust },
+        { action: row.action, scoreType: 'intimacy', value: row.intimacy },
+        { action: row.action, scoreType: 'communication', value: row.communication }
+      ]
+      var promises = items.map(function(item) {
+        return updateChallengeScoreConfig(item)
+      })
+      Promise.all(promises).then(function() {
+        self.$message.success(row.action + ' 配置已保存')
+      }).catch(function() {
+        self.$message.error('保存失败')
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.header-tip { color: #909399; font-size: 13px; margin-top: 4px; }
+.footer-tip { margin-top: 12px; color: #909399; font-size: 12px; line-height: 1.6; }
+</style>