Ver Fonte

红娘工作台编辑资料

mazhenhang há 1 mês atrás
pai
commit
7d15f1a98f

+ 316 - 234
LiangZhiYUMao/pages/matchmaker-workbench/edit-profile.vue

@@ -1,247 +1,306 @@
 <template>
   <view class="edit-profile">
-  <!-- 顶部导航栏 -->
-  <view class="header">
-    <view class="back-btn" @click="handleBack">
-      <text class="back-icon">←</text>
+    <!-- 顶部导航栏 -->
+    <view class="header">
+      <view class="back-btn" @click="handleBack">
+        <text class="back-icon">←</text>
+      </view>
+      <text class="header-title">编辑资料</text>
+      <view class="right-empty"></view>
     </view>
-    <text class="header-title">编辑资料</text>
-    <view class="right-empty"></view>
-  </view>
 
-  <scroll-view scroll-y class="content">
-    <!-- 头像上传区域 -->
-    <view class="avatar-section">
-      <view class="avatar-container">
-        <image class="avatar" :src="userInfo.avatar || defaultAvatar" mode="aspectFill"></image>
-        <view class="avatar-upload-btn" @click="handleAvatarUpload">
-          <text class="upload-icon">📷</text>
+    <scroll-view scroll-y class="content">
+      <!-- 头像展示区域(不可修改) -->
+      <view class="avatar-section">
+        <view class="avatar-container">
+          <image class="avatar" :src="userInfo.avatar || defaultAvatar" mode="aspectFill"></image>
         </view>
+        <text class="avatar-text">头像由平台统一设置,暂不支持修改</text>
       </view>
-      <text class="avatar-text">更换头像</text>
-    </view>
 
-    <!-- 表单区域 -->
-    <view class="form-section">
-      <!-- 姓名 -->
-      <view class="form-item">
-        <view class="form-label">
-          <text class="label-icon">👤</text>
-          <text class="label-text">姓名</text>
+      <!-- 表单区域 -->
+      <view class="form-section">
+        <!-- 真实姓名 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">👤</text>
+            <text class="label-text">真实姓名</text>
+          </view>
+          <input
+              class="form-input"
+              v-model="userInfo.real_name"
+              placeholder="请输入真实姓名"
+              placeholder-style="color: #999"
+          >
         </view>
-        <input
-            class="form-input"
-            v-model="userInfo.name"
-            placeholder="请输入姓名"
-            placeholder-style="color: #999"
-        >
-      </view>
 
-      <!-- 性别 -->
-      <view class="form-item">
-        <view class="form-label">
-          <text class="label-icon">⚧️</text>
-          <text class="label-text">性别</text>
+        <!-- 性别 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">⚧️</text>
+            <text class="label-text">性别</text>
+          </view>
+          <picker class="form-picker gender-picker" @change="handleGenderChange" :value="genderIndex"
+                  :range="genderOptions">
+            <view class="picker-content">
+              {{ genderLabel }}
+            </view>
+          </picker>
         </view>
-        <picker class="form-picker gender-picker" @change="handleGenderChange" :value="genderIndex"
-                :range="genderOptions">
-          <view class="picker-content">
-            {{ userInfo.gender || '请选择性别' }}
+
+        <!-- 出生日期 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">�</text>
+            <text class="label-text">出生日期</text>
           </view>
-        </picker>
-      </view>
+          <picker class="form-picker" mode="date" :value="userInfo.birth_date" @change="handleBirthChange">
+            <view class="picker-content">
+              {{ userInfo.birth_date || '请选择出生日期' }}
+            </view>
+          </picker>
+        </view>
 
-      <!-- 个人简介 -->
-      <view class="form-item">
-        <view class="form-label">
-          <text class="label-icon">📝</text>
-          <text class="label-text">个人简介</text>
+        <!-- 邮箱 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">📧</text>
+            <text class="label-text">邮箱</text>
+          </view>
+          <input
+              class="form-input"
+              v-model="userInfo.email"
+              placeholder="请输入邮箱"
+              placeholder-style="color: #999"
+              type="email"
+          >
         </view>
-        <textarea
-            class="form-textarea"
-            v-model="userInfo.bio"
-            placeholder="请输入个人简介"
-            placeholder-style="color: #999"
-            maxlength="200"
-            auto-height
-        ></textarea>
-      </view>
 
-      <!-- 邮箱 -->
-      <view class="form-item">
-        <view class="form-label">
-          <text class="label-icon">📧</text>
-          <text class="label-text">邮箱</text>
+        <!-- 所在地区 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">📍</text>
+            <text class="label-text">所在地区</text>
+          </view>
+          <picker
+              class="form-picker"
+              mode="multiSelector"
+              :value="multiIndex"
+              :range="multiArray"
+              @columnchange="handleColumnChange"
+              @change="handleMultiPickerChange"
+          >
+            <view class="picker-content">{{ selectedArea || '请选择地区' }}</view>
+          </picker>
         </view>
-        <input
-            class="form-input"
-            v-model="userInfo.email"
-            placeholder="请输入邮箱"
-            placeholder-style="color: #999"
-            type="email"
-        >
-      </view>
 
-      <!-- 省市区选择 -->
-      <view class="form-item">
-        <view class="form-label">
-          <text class="label-icon">📍</text>
-          <text class="label-text">所在地区</text>
+        <!-- 详细地址 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">🏠</text>
+            <text class="label-text">详细地址</text>
+          </view>
+          <textarea
+              class="form-textarea"
+              v-model="userInfo.address_detail"
+              placeholder="请输入详细地址"
+              placeholder-style="color: #999"
+              maxlength="255"
+              auto-height
+          ></textarea>
         </view>
-        <picker
-            class="form-picker"
-            mode="multiSelector"
-            :value="multiIndex"
-            :range="multiArray"
-            @columnchange="handleColumnChange"
-            @change="handleMultiPickerChange"
-        >
-          <view class="picker-content">{{ selectedArea || '请选择地区' }}</view>
-        </picker>
-      </view>
 
-      <!-- 详细地址 -->
-      <view class="form-item">
-        <view class="form-label">
-          <text class="label-icon">🏠</text>
-          <text class="label-text">详细地址</text>
+        <!-- 个人简介 -->
+        <view class="form-item">
+          <view class="form-label">
+            <text class="label-icon">📝</text>
+            <text class="label-text">个人简介</text>
+          </view>
+          <textarea
+              class="form-textarea"
+              v-model="userInfo.profile"
+              placeholder="请输入个人简介"
+              placeholder-style="color: #999"
+              maxlength="200"
+              auto-height
+          ></textarea>
         </view>
-        <textarea
-            class="form-textarea"
-            v-model="userInfo.address_detail"
-            placeholder="请输入详细地址"
-            placeholder-style="color: #999"
-            maxlength="255"
-            auto-height
-        ></textarea>
       </view>
-    </view>
 
-    <!-- 保存按钮 -->
-    <view class="save-btn-section">
-      <button class="save-btn" @click="handleSave">保存</button>
-    </view>
-  </scroll-view>
+      <!-- 保存按钮 -->
+      <view class="save-btn-section">
+        <button class="save-btn" @click="handleSave">保存</button>
+      </view>
+    </scroll-view>
   </view>
 </template>
 
 <script>
+import api from '../../utils/api.js'
+
 export default {
   data() {
     return {
       // 默认头像
       defaultAvatar: 'https://q.qlogo.cn/qqapp/1105591438/05E13358B43B3D39D6AC2D6888828201/100',
 
-      // 用户信息
+      // 用户信息(与后端字段基本对应)
       userInfo: {
         avatar: '',
-        name: '',
-        gender: '',
-        bio: '我是一名专业的红娘,拥有丰富的婚恋服务经验,已成功帮助1200对单身男女找到幸福。',
+        real_name: '',
+        gender: null, // 1: 男, 2: 女
+        birth_date: '',
         email: '',
-        province_id: '',
-        city_id: '',
-        area_id: '',
+        province_id: null,
+        city_id: null,
+        area_id: null,
         address_detail: '',
         province_name: '',
         city_name: '',
-        area_name: ''
+        area_name: '',
+        profile: ''
       },
+      // 当前红娘ID(用于更新)
+      matchmakerId: null,
 
-      // 省市区多列选择器数据
+      // 省市区多列选择器数据(名称数组)
       multiArray: [
-        ['北京市', '天津市', '河北省', '山西省', '内蒙古自治区'],
-        ['石家庄市', '唐山市', '秦皇岛市', '邯郸市', '邢台市', '保定市'],
-        ['竞秀区', '莲池区', '满城区', '清苑区', '徐水区', '涞水县']
+        [], // 省
+        [], // 市
+        []  // 区
       ],
       multiIndex: [0, 0, 0],
       selectedArea: '',
-
-      // 省市区详细数据(用于动态加载)
-      areaData: {
-        '北京市': {
-          '北京市': ['东城区', '西城区', '朝阳区', '海淀区', '丰台区', '石景山区']
-        },
-        '天津市': {
-          '天津市': ['和平区', '河东区', '河西区', '南开区', '河北区', '红桥区']
-        },
-        '河北省': {
-          '石家庄市': ['长安区', '桥西区', '新华区', '井陉矿区', '裕华区', '藁城区'],
-          '唐山市': ['路南区', '路北区', '古冶区', '开平区', '丰南区', '丰润区'],
-          '秦皇岛市': ['海港区', '山海关区', '北戴河区', '抚宁区', '青龙满族自治县'],
-          '邯郸市': ['邯山区', '丛台区', '复兴区', '峰峰矿区', '肥乡区', '永年区'],
-          '邢台市': ['桥东区', '桥西区', '邢台县', '临城县', '内丘县', '柏乡县'],
-          '保定市': ['竞秀区', '莲池区', '满城区', '清苑区', '徐水区', '涞水县']
-        },
-        '山西省': {
-          '太原市': ['小店区', '迎泽区', '杏花岭区', '尖草坪区', '万柏林区', '晋源区']
-        },
-        '内蒙古自治区': {
-          '呼和浩特市': ['新城区', '回民区', '玉泉区', '赛罕区', '土默特左旗']
-        }
-      },
+      // 后端返回的省市区原始列表(包含id和name)
+      provinces: [],
+      cities: [],
+      areas: [],
 
       // 性别选项
       genderOptions: ['男', '女'],
-      genderIndex: 0
+      genderIndex: -1
+    }
+  },
+  computed: {
+    genderLabel() {
+      if (this.userInfo.gender === 1) return '男'
+      if (this.userInfo.gender === 2) return '女'
+      return '请选择性别'
     }
   },
   onLoad() {
-    // 从本地存储获取用户信息
-    this.loadUserInfo()
+    this.initPage()
   },
   methods: {
-    // 加载用户信息
-    loadUserInfo() {
-      const userInfo = uni.getStorageSync('matchmakerUserInfo')
-      if (userInfo) {
-        this.userInfo = {...this.userInfo, ...userInfo}
-        // 设置性别索引
-        this.genderIndex = this.genderOptions.indexOf(this.userInfo.gender) || 0
-
-        // 如果有省市区信息,设置选中的区域文本
-        if (this.userInfo.province_name && this.userInfo.city_name && this.userInfo.area_name) {
-          this.selectedArea = `${this.userInfo.province_name} ${this.userInfo.city_name} ${this.userInfo.area_name}`
-
-          // 设置对应的索引
-          const provinceIndex = this.multiArray[0].indexOf(this.userInfo.province_name)
-          if (provinceIndex !== -1) {
-            this.multiIndex[0] = provinceIndex
-            // 更新城市列表
-            this.updateCityList(provinceIndex)
-
-            const cityIndex = this.multiArray[1].indexOf(this.userInfo.city_name)
-            if (cityIndex !== -1) {
-              this.multiIndex[1] = cityIndex
-              // 更新区县列表
-              this.updateAreaList(provinceIndex, cityIndex)
-
-              const areaIndex = this.multiArray[2].indexOf(this.userInfo.area_name)
-              if (areaIndex !== -1) {
-                this.multiIndex[2] = areaIndex
-              }
-            }
+    async initPage() {
+      await this.loadUserInfo()
+      await this.loadProvincesAndInit()
+    },
+
+    // 加载红娘资料
+    async loadUserInfo() {
+      try {
+        const basicUser = uni.getStorageSync('userInfo')
+        const userId = basicUser && basicUser.userId ? basicUser.userId : null
+        if (!userId) {
+          uni.showToast({ title: '未登录,无法加载资料', icon: 'none' })
+          return
+        }
+
+        const matchmakerInfo = await api.matchmaker.getByUserId(userId)
+        if (matchmakerInfo) {
+          // 映射后端字段到本地表单
+          this.matchmakerId = matchmakerInfo.matchmakerId || matchmakerInfo.matchmaker_id || null
+          this.userInfo.avatar = matchmakerInfo.avatar_url || ''
+          this.userInfo.real_name = matchmakerInfo.real_name || ''
+          this.userInfo.gender = typeof matchmakerInfo.gender === 'number' ? matchmakerInfo.gender : null
+          this.userInfo.birth_date = matchmakerInfo.birth_date || ''
+          this.userInfo.email = matchmakerInfo.email || ''
+          this.userInfo.address_detail = matchmakerInfo.address_detail || ''
+          this.userInfo.province_id = matchmakerInfo.province_id || null
+          this.userInfo.city_id = matchmakerInfo.city_id || null
+          this.userInfo.area_id = matchmakerInfo.area_id || null
+          this.userInfo.province_name = matchmakerInfo.province_name || ''
+          this.userInfo.city_name = matchmakerInfo.city_name || ''
+          this.userInfo.area_name = matchmakerInfo.area_name || ''
+          this.userInfo.profile = matchmakerInfo.profile || ''
+
+          // 性别索引
+          if (this.userInfo.gender === 1) this.genderIndex = 0
+          else if (this.userInfo.gender === 2) this.genderIndex = 1
+
+          // 如果后端返回了省市区名称,直接使用
+          if (matchmakerInfo.province_name && matchmakerInfo.city_name && matchmakerInfo.area_name) {
+            this.selectedArea = `${matchmakerInfo.province_name} ${matchmakerInfo.city_name} ${matchmakerInfo.area_name}`
           }
         }
+      } catch (e) {
+        console.error('加载红娘资料失败', e)
+        uni.showToast({ title: '加载资料失败', icon: 'none' })
+      }
+    },
+    // 从后端加载省列表并根据已有省市区初始化
+    async loadProvincesAndInit() {
+      try {
+        const list = await api.area.getProvinces()
+        this.provinces = list || []
+        this.multiArray[0] = this.provinces.map(p => p.name)
+
+        if (this.userInfo.province_id) {
+          const pIndex = this.provinces.findIndex(p => p.id === this.userInfo.province_id)
+          if (pIndex >= 0) {
+            this.multiIndex[0] = pIndex
+            await this.updateCityList(pIndex, true)
+          }
+        }
+      } catch (e) {
+        console.error('加载省份列表失败', e)
       }
     },
 
-    // 更新城市列表
-    updateCityList(provinceIndex) {
-      const province = this.multiArray[0][provinceIndex]
-      const cities = Object.keys(this.areaData[province] || {})
-      this.multiArray[1] = cities
-      this.multiIndex[1] = 0
-      this.updateAreaList(provinceIndex, 0)
+    // 更新城市列表(根据省索引),init 表示是否按已有 city_id 初始化
+    async updateCityList(provinceIndex, init = false) {
+      const province = this.provinces[provinceIndex]
+      if (!province) return
+      try {
+        const list = await api.area.getCities(province.id)
+        this.cities = list || []
+        this.multiArray[1] = this.cities.map(c => c.name)
+        this.multiArray[2] = []
+        this.multiIndex[1] = 0
+        this.multiIndex[2] = 0
+
+        if (init && this.userInfo.city_id) {
+          const cIndex = this.cities.findIndex(c => c.id === this.userInfo.city_id)
+          if (cIndex >= 0) {
+            this.multiIndex[1] = cIndex
+            await this.updateAreaList(provinceIndex, cIndex, true)
+          }
+        }
+      } catch (e) {
+        console.error('加载城市列表失败', e)
+      }
     },
 
     // 更新区县列表
-    updateAreaList(provinceIndex, cityIndex) {
-      const province = this.multiArray[0][provinceIndex]
-      const city = this.multiArray[1][cityIndex]
-      const areas = this.areaData[province]?.[city] || []
-      this.multiArray[2] = areas
-      this.multiIndex[2] = 0
+    async updateAreaList(provinceIndex, cityIndex, init = false) {
+      const city = this.cities[cityIndex]
+      if (!city) return
+      try {
+        const list = await api.area.getAreas(city.id)
+        this.areas = list || []
+        this.multiArray[2] = this.areas.map(a => a.name)
+        this.multiIndex[2] = 0
+
+        if (init && this.userInfo.area_id) {
+          const aIndex = this.areas.findIndex(a => a.id === this.userInfo.area_id)
+          if (aIndex >= 0) {
+            this.multiIndex[2] = aIndex
+          }
+        }
+      } catch (e) {
+        console.error('加载区县列表失败', e)
+      }
     },
 
     // 处理列变化
@@ -253,29 +312,35 @@ export default {
       // 根据列索引更新对应的数据
       if (column === 0) {
         // 省份变化,更新城市和区县
-        this.updateCityList(value)
+        this.updateCityList(value, false)
       } else if (column === 1) {
         // 城市变化,更新区县
-        this.updateAreaList(this.multiIndex[0], value)
+        this.updateAreaList(this.multiIndex[0], value, false)
       }
     },
 
     // 处理多列选择器确认
     handleMultiPickerChange(e) {
       this.multiIndex = e.detail.value
-      const province = this.multiArray[0][this.multiIndex[0]]
-      const city = this.multiArray[1][this.multiIndex[1]]
-      const area = this.multiArray[2][this.multiIndex[2]]
-
-      this.selectedArea = `${province} ${city} ${area}`
-
-      // 更新用户信息
-      this.userInfo.province_id = this.multiIndex[0] + 1
-      this.userInfo.city_id = this.multiIndex[1] + 1
-      this.userInfo.area_id = this.multiIndex[2] + 1
-      this.userInfo.province_name = province
-      this.userInfo.city_name = city
-      this.userInfo.area_name = area
+      const p = this.provinces[this.multiIndex[0]]
+      const c = this.cities[this.multiIndex[1]]
+      const a = this.areas[this.multiIndex[2]]
+
+      const provinceName = p ? p.name : ''
+      const cityName = c ? c.name : ''
+      const areaName = a ? a.name : ''
+
+      this.selectedArea = provinceName && cityName && areaName
+        ? `${provinceName} ${cityName} ${areaName}`
+        : ''
+
+      // 更新用户信息中的 id 和名称
+      this.userInfo.province_id = p ? p.id : null
+      this.userInfo.city_id = c ? c.id : null
+      this.userInfo.area_id = a ? a.id : null
+      this.userInfo.province_name = provinceName
+      this.userInfo.city_name = cityName
+      this.userInfo.area_name = areaName
     },
 
     // 返回上一页
@@ -285,37 +350,24 @@ export default {
       })
     },
 
-    // 更换头像
-    handleAvatarUpload() {
-      uni.chooseImage({
-        count: 1,
-        sizeType: ['compressed'],
-        sourceType: ['album', 'camera'],
-        success: (res) => {
-          const tempFilePath = res.tempFilePaths[0]
-          // 这里可以添加上传图片到服务器的逻辑
-          this.userInfo.avatar = tempFilePath
-          uni.showToast({
-            title: '头像更换成功',
-            icon: 'success'
-          })
-        }
-      })
-    },
-
     // 性别选择变化
     handleGenderChange(e) {
-      const index = e.detail.value
+      const index = Number(e.detail.value)
       this.genderIndex = index
-      this.userInfo.gender = this.genderOptions[index]
+      this.userInfo.gender = index >= 0 ? index + 1 : null
+    },
+
+    // 出生日期选择
+    handleBirthChange(e) {
+      this.userInfo.birth_date = e.detail.value
     },
 
     // 保存资料
-    handleSave() {
+    async handleSave() {
       // 验证表单
-      if (!this.userInfo.name) {
+      if (!this.userInfo.real_name) {
         uni.showToast({
-          title: '请输入姓名',
+          title: '请输入真实姓名',
           icon: 'none'
         })
         return
@@ -329,19 +381,49 @@ export default {
         return
       }
 
-      // 保存到本地存储
-      uni.setStorageSync('matchmakerUserInfo', this.userInfo)
+      // 简单邮箱格式校验(若填写)
+      if (this.userInfo.email) {
+        const emailRegex = /^[^@\s]+@[^@\s]+\.[^@\s]+$/
+        if (!emailRegex.test(this.userInfo.email)) {
+          uni.showToast({ title: '邮箱格式不正确', icon: 'none' })
+          return
+        }
+      }
 
-      // 提示保存成功
-      uni.showToast({
-        title: '资料保存成功',
-        icon: 'success'
-      })
+      try {
+        if (!this.matchmakerId) {
+          uni.showToast({ title: '红娘信息缺失,无法保存', icon: 'none' })
+          return
+        }
 
-      // 返回上一页
-      setTimeout(() => {
-        this.handleBack()
-      }, 1500)
+        const payload = {
+          // 注意:后端 Jackson 使用了 SNAKE_CASE 策略
+          // email、gender、profile 名字不变,其他字段用下划线命名
+          email: this.userInfo.email,
+          real_name: this.userInfo.real_name,
+          gender: this.userInfo.gender,
+          birth_date: this.userInfo.birth_date,
+          address_detail: this.userInfo.address_detail,
+          province_id: this.userInfo.province_id,
+          city_id: this.userInfo.city_id,
+          area_id: this.userInfo.area_id,
+          profile: this.userInfo.profile
+        }
+
+        await api.matchmaker.updateProfile(this.matchmakerId, payload)
+
+        uni.showToast({
+          title: '资料保存成功',
+          icon: 'success'
+        })
+
+        setTimeout(() => {
+          this.handleBack()
+        }, 1200)
+      } catch (e) {
+        console.error('保存红娘资料失败', e)
+        uni.showToast({ title: '保存失败,请稍后重试', icon: 'none' })
+      }
     }
   }
 }

+ 61 - 13
LiangZhiYUMao/pages/matchmaker-workbench/mine.vue

@@ -192,10 +192,6 @@
 					isSignedToday: false,
 					continuousDays: 0,
 					totalDays: 0,
-					// 日历相关
-					currentDate: new Date(),
-					calendarTitle: '',
-					calendarDays: []
 				}
 			},
 		onLoad() {
@@ -203,11 +199,15 @@
 			this.checkSignInStatus()
 			this.generateCalendar()
 		},
+		onShow() {
+			// 从编辑资料页返回时,自动刷新个人资料
+			this.loadProfileData()
+		},
 		methods: {
-			// 生成日历数据
-			generateCalendar() {
-				const date = this.currentDate || new Date()
-				const year = date.getFullYear()
+			// ...
+		}
+	}
+</script>
 				const month = date.getMonth()
 				
 				// 设置日历标题
@@ -341,7 +341,28 @@
 						// 调用API检查签到状态
 						const res = await api.matchmaker.checkinStatus(userId)
 						console.log('检查签到状态返回结果:', res)
-						this.isSignedToday = res || false
+						// 只在明确标记为已签到时才置为 true,避免对象/字符串被当成 true
+						let signed = false
+						if (typeof res === 'boolean') {
+							signed = res
+						} else if (typeof res === 'string') {
+							// 后端可能返回 'true' / 'false'
+							signed = res === 'true'
+						} else if (typeof res === 'number') {
+							// 约定 1 表示已签到,0 表示未签到
+							signed = res === 1
+						} else if (res && typeof res === 'object') {
+							// 常见结构兼容:{ todaySigned: true } / { signed: true } / { isSignedToday: true }
+							if (typeof res.todaySigned === 'boolean') signed = res.todaySigned
+							else if (typeof res.signed === 'boolean') signed = res.signed
+							else if (typeof res.isSignedToday === 'boolean') signed = res.isSignedToday
+							else if (typeof res.data === 'object' && res.data) {
+								if (typeof res.data.todaySigned === 'boolean') signed = res.data.todaySigned
+								else if (typeof res.data.signed === 'boolean') signed = res.data.signed
+								else if (typeof res.data.isSignedToday === 'boolean') signed = res.data.isSignedToday
+							}
+						}
+						this.isSignedToday = !!signed
 						
 						// 获取签到统计
 						const stats = await api.matchmaker.checkinStats(userId)
@@ -386,8 +407,29 @@
 						// 调用签到API
 						const res = await api.matchmaker.doCheckin(userId)
 						console.log('签到API返回结果:', res)
-						// 直接使用返回的布尔值判断签到结果
-						if (res) {
+						// 解析返回结果,兼容多种返回结构
+						let success = false
+						let alreadySigned = false
+						if (typeof res === 'boolean') {
+							// true 表示本次签到成功,false 表示失败或已签到
+							success = res
+						} else if (typeof res === 'string') {
+							// 'ok' / 'success' 之类
+							success = res === 'true' || res === 'ok' || res === 'success'
+						} else if (res && typeof res === 'object') {
+							// { success: true, todaySigned: true } 等结构
+							if (typeof res.success === 'boolean') success = res.success
+							if (typeof res.todaySigned === 'boolean') alreadySigned = res.todaySigned
+							else if (typeof res.signed === 'boolean') alreadySigned = res.signed
+							else if (typeof res.isSignedToday === 'boolean') alreadySigned = res.isSignedToday
+							if (res.data && typeof res.data === 'object') {
+								if (typeof res.data.success === 'boolean') success = res.data.success
+								if (typeof res.data.todaySigned === 'boolean') alreadySigned = res.data.todaySigned
+								else if (typeof res.data.signed === 'boolean') alreadySigned = res.data.signed
+								else if (typeof res.data.isSignedToday === 'boolean') alreadySigned = res.data.isSignedToday
+							}
+						}
+						if (success) {
 							// 签到成功
 							uni.showToast({
 								title: '签到成功',
@@ -401,12 +443,18 @@
 							this.loadProfileData()
 							// 关闭弹框
 							this.closeSignInPopup()
-						} else {
-							// 今日已签到
+						} else if (alreadySigned) {
+							// 接口明确表示今日已签到
 							uni.showToast({
 								title: '今日已签到',
 								icon: 'none'
 							})
+						} else {
+							// 签到失败
+							uni.showToast({
+								title: '签到失败,请稍后重试',
+								icon: 'none'
+							})
 						}
 					} else {
 						// 没有有效的userId,提示用户登录

+ 7 - 0
LiangZhiYUMao/utils/api.js

@@ -319,6 +319,13 @@ export default {
         doCheckin: (makerId) => request({ 
             url: `/matchmaker/checkin/do?makerId=${makerId}`, 
             method: 'POST' 
+        }),
+
+        // 更新红娘资料(编辑资料页使用)
+        updateProfile: (matchmakerId, data) => request({
+            url: `/matchmaker/update/${matchmakerId}`,
+            method: 'PUT',
+            data
         })
   },
 

+ 1 - 0
service/admin/src/main/java/com/zhentao/entity/Matchmaker.java

@@ -36,6 +36,7 @@ public class Matchmaker implements Serializable {
     private String avatarUrl;
     private Integer matchmakerType;
     private Integer level;
+    private Integer points;  // 积分
     private Integer successCouples;
     private Integer provinceId;
     private Integer cityId;

+ 60 - 60
service/admin/src/main/java/com/zhentao/service/impl/PointsMallServiceImpl.java

@@ -19,67 +19,67 @@ import java.util.*;
  */
 @Service
 public class PointsMallServiceImpl implements PointsMallService {
-    
+
     @Autowired
     private PointsProductMapper pointsProductMapper;
-    
+
     @Autowired
     private PointsOrderMapper pointsOrderMapper;
-    
+
     @Autowired
     private PointsDetailMapper pointsDetailMapper;
-    
+
     @Autowired
     private PointRuleMapper pointRuleMapper;
-    
+
     @Autowired
     private MatchmakerMapper matchmakerMapper;
-    
+
     @Override
     public Map<String, Object> getProductList(Integer category, Integer pageNum, Integer pageSize) {
         Page<PointsProduct> page = new Page<>(pageNum, pageSize);
         LambdaQueryWrapper<PointsProduct> wrapper = new LambdaQueryWrapper<>();
-        
+
         // 只查询上架的商品
         wrapper.eq(PointsProduct::getStatus, 1);
-        
+
         // 按分类筛选
         if (category != null && category > 0) {
             wrapper.eq(PointsProduct::getCategory, category);
         }
-        
+
         // 按排序和创建时间排序
         wrapper.orderByDesc(PointsProduct::getSortOrder)
-               .orderByDesc(PointsProduct::getCreateTime);
-        
+                .orderByDesc(PointsProduct::getCreateTime);
+
         Page<PointsProduct> result = pointsProductMapper.selectPage(page, wrapper);
-        
+
         Map<String, Object> data = new HashMap<>();
         data.put("list", result.getRecords());
         data.put("total", result.getTotal());
         data.put("pageNum", pageNum);
         data.put("pageSize", pageSize);
         data.put("pages", result.getPages());
-        
+
         return data;
     }
-    
+
     @Override
     public List<PointsProduct> getRecommendProducts(Integer limit) {
         LambdaQueryWrapper<PointsProduct> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointsProduct::getStatus, 1)
-               .eq(PointsProduct::getIsRecommend, 1)
-               .orderByDesc(PointsProduct::getSortOrder)
-               .last("LIMIT " + (limit != null ? limit : 10));
-        
+                .eq(PointsProduct::getIsRecommend, 1)
+                .orderByDesc(PointsProduct::getSortOrder)
+                .last("LIMIT " + (limit != null ? limit : 10));
+
         return pointsProductMapper.selectList(wrapper);
     }
-    
+
     @Override
     public PointsProduct getProductDetail(Long productId) {
         return pointsProductMapper.selectById(productId);
     }
-    
+
     @Override
     public Integer getPointsBalance(Long makerId) {
         Matchmaker matchmaker = matchmakerMapper.selectById(makerId);
@@ -94,74 +94,74 @@ public class PointsMallServiceImpl implements PointsMallService {
         LambdaQueryWrapper<PointsDetail> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointsDetail::getMakerId, makerId);
         List<PointsDetail> details = pointsDetailMapper.selectList(wrapper);
-        
+
         int balance = 0;
         for (PointsDetail detail : details) {
             balance += detail.getPoints();
         }
         return balance;
     }
-    
+
     @Override
     public Map<String, Object> getPointsRecords(Long makerId, Integer pageNum, Integer pageSize) {
         Page<PointsDetail> page = new Page<>(pageNum, pageSize);
         LambdaQueryWrapper<PointsDetail> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointsDetail::getMakerId, makerId)
-               .orderByDesc(PointsDetail::getCreateTime);
-        
+                .orderByDesc(PointsDetail::getCreateTime);
+
         Page<PointsDetail> result = pointsDetailMapper.selectPage(page, wrapper);
-        
+
         Map<String, Object> data = new HashMap<>();
         data.put("list", result.getRecords());
         data.put("total", result.getTotal());
         data.put("pageNum", pageNum);
         data.put("pageSize", pageSize);
-        
+
         return data;
     }
-    
+
     @Override
     public List<PointRule> getPointRules() {
         LambdaQueryWrapper<PointRule> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointRule::getStatus, 1)
-               .orderByAsc(PointRule::getRuleType);
+                .orderByAsc(PointRule::getRuleType);
         return pointRuleMapper.selectList(wrapper);
     }
-    
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public PointsOrder exchangeProduct(Long makerId, Long productId, Integer quantity,
-                                        String receiverName, String receiverPhone, String receiverAddress) {
+                                       String receiverName, String receiverPhone, String receiverAddress) {
         // 1. 检查商品是否存在且上架
         PointsProduct product = pointsProductMapper.selectById(productId);
         if (product == null || product.getStatus() != 1) {
             throw new RuntimeException("商品不存在或已下架");
         }
-        
+
         // 2. 检查库存
         if (product.getStock() < quantity) {
             throw new RuntimeException("商品库存不足");
         }
-        
+
         // 3. 计算所需积分
         int totalPoints = product.getPointsPrice() * quantity;
-        
+
         // 4. 检查积分余额
         Integer balance = getPointsBalance(makerId);
         if (balance < totalPoints) {
             throw new RuntimeException("积分余额不足,当前积分: " + balance + ",需要: " + totalPoints);
         }
-        
+
         // 5. 扣减库存
         LambdaUpdateWrapper<PointsProduct> productUpdate = new LambdaUpdateWrapper<>();
         productUpdate.eq(PointsProduct::getId, productId)
-                     .ge(PointsProduct::getStock, quantity)
-                     .setSql("stock = stock - " + quantity);
+                .ge(PointsProduct::getStock, quantity)
+                .setSql("stock = stock - " + quantity);
         int updateCount = pointsProductMapper.update(null, productUpdate);
         if (updateCount == 0) {
             throw new RuntimeException("库存扣减失败,请重试");
         }
-        
+
         // 6. 创建订单
         PointsOrder order = new PointsOrder();
         order.setOrderNo(generateOrderNo());
@@ -178,9 +178,9 @@ public class PointsMallServiceImpl implements PointsMallService {
         order.setReceiverAddress(receiverAddress);
         order.setCreateTime(LocalDateTime.now());
         order.setUpdateTime(LocalDateTime.now());
-        
+
         pointsOrderMapper.insert(order);
-        
+
         // 7. 记录积分变动(扣减)
         PointsDetail detail = new PointsDetail();
         detail.setMakerId(makerId);
@@ -188,63 +188,63 @@ public class PointsMallServiceImpl implements PointsMallService {
         detail.setReason("兑换商品: " + product.getName() + " x" + quantity);
         detail.setCreateTime(LocalDateTime.now());
         detail.setUpdateTime(LocalDateTime.now());
-        
+
         pointsDetailMapper.insert(detail);
-        
+
         // 8. 更新红娘积分余额
         LambdaUpdateWrapper<Matchmaker> makerUpdate = new LambdaUpdateWrapper<>();
         makerUpdate.eq(Matchmaker::getMatchmakerId, makerId)
-                   .setSql("points = IFNULL(points, 0) - " + totalPoints);
+                .setSql("points = IFNULL(points, 0) - " + totalPoints);
         matchmakerMapper.update(null, makerUpdate);
-        
+
         return order;
     }
-    
+
     @Override
     public Map<String, Object> getOrderList(Long makerId, Integer status, Integer pageNum, Integer pageSize) {
         Page<PointsOrder> page = new Page<>(pageNum, pageSize);
         LambdaQueryWrapper<PointsOrder> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointsOrder::getMakerId, makerId);
-        
+
         if (status != null && status >= 0) {
             wrapper.eq(PointsOrder::getStatus, status);
         }
-        
+
         wrapper.orderByDesc(PointsOrder::getCreateTime);
-        
+
         Page<PointsOrder> result = pointsOrderMapper.selectPage(page, wrapper);
-        
+
         Map<String, Object> data = new HashMap<>();
         data.put("list", result.getRecords());
         data.put("total", result.getTotal());
         data.put("pageNum", pageNum);
         data.put("pageSize", pageSize);
-        
+
         return data;
     }
-    
+
     @Override
     public PointsOrder getOrderDetail(String orderNo) {
         LambdaQueryWrapper<PointsOrder> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointsOrder::getOrderNo, orderNo);
         return pointsOrderMapper.selectOne(wrapper);
     }
-    
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Integer addPoints(Long makerId, Integer ruleType, String reason) {
         // 1. 查询积分规则
         LambdaQueryWrapper<PointRule> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(PointRule::getRuleType, ruleType)
-               .eq(PointRule::getStatus, 1);
+                .eq(PointRule::getStatus, 1);
         PointRule rule = pointRuleMapper.selectOne(wrapper);
-        
+
         if (rule == null) {
             throw new RuntimeException("积分规则不存在");
         }
-        
+
         int pointValue = rule.getPointValue();
-        
+
         // 2. 记录积分变动
         PointsDetail detail = new PointsDetail();
         detail.setMakerId(makerId);
@@ -252,18 +252,18 @@ public class PointsMallServiceImpl implements PointsMallService {
         detail.setReason(reason != null ? reason : rule.getRuleName());
         detail.setCreateTime(LocalDateTime.now());
         detail.setUpdateTime(LocalDateTime.now());
-        
+
         pointsDetailMapper.insert(detail);
-        
+
         // 3. 更新红娘积分余额
         LambdaUpdateWrapper<Matchmaker> makerUpdate = new LambdaUpdateWrapper<>();
         makerUpdate.eq(Matchmaker::getMatchmakerId, makerId)
-                   .setSql("points = IFNULL(points, 0) + " + pointValue);
+                .setSql("points = IFNULL(points, 0) + " + pointValue);
         matchmakerMapper.update(null, makerUpdate);
-        
+
         return pointValue;
     }
-    
+
     /**
      * 生成订单编号
      */