Files
happy-life-star/docs/superpowers/plans/2026-06-27-sms-login-config.md
T

29 KiB
Raw Blame History

短信登录开关与通用系统配置 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 新建通用系统配置表和管理后台设置页面,通过「短信登录开关」控制小程序登录页是否显示手机号短信登录入口。

Architecture: 新建 t_system_config key-value 配置表,后端提供管理端 CRUD 接口和公开查询接口,管理后台新增「系统设置」页面动态渲染配置项,小程序登录页调用公开接口获取登录方式配置并条件渲染。

Tech Stack: Spring Boot 2.7 + MyBatis-Plus + Redis / Vue 3 + Element Plus / UniApp (Vue 3)

Spec: docs/superpowers/specs/2026-06-27-sms-login-config-design.md


Task 1: 数据库建表和初始数据

Files:

  • Execute: SQL on MySQL database

  • Step 1: 执行建表 SQL

CREATE TABLE t_system_config (
  id            VARCHAR(64)  PRIMARY KEY,
  config_key    VARCHAR(100) NOT NULL UNIQUE COMMENT '配置唯一标识',
  config_value  VARCHAR(500) NOT NULL DEFAULT '' COMMENT '配置值',
  value_type    VARCHAR(20)  NOT NULL DEFAULT 'string' COMMENT '值类型: boolean/string/number/json',
  config_group  VARCHAR(50)  NOT NULL DEFAULT 'system' COMMENT '配置分组: system/login/notification',
  config_name   VARCHAR(100) NOT NULL DEFAULT '' COMMENT '显示名称',
  description   VARCHAR(300) DEFAULT '' COMMENT '配置说明',
  sort_order    INT          NOT NULL DEFAULT 0 COMMENT '排序号',
  is_visible    TINYINT      NOT NULL DEFAULT 1 COMMENT '是否在管理后台可见: 0-隐藏, 1-显示',
  create_by     VARCHAR(64)  DEFAULT NULL,
  create_time   DATETIME     DEFAULT NULL,
  update_by     VARCHAR(64)  DEFAULT NULL,
  update_time   DATETIME     DEFAULT NULL,
  is_deleted    TINYINT      NOT NULL DEFAULT 0
) COMMENT = '系统配置表';
  • Step 2: 插入初始数据(短信登录开关)
INSERT INTO t_system_config (id, config_key, config_value, value_type, config_group, config_name, description, sort_order, is_visible, create_time, update_time, is_deleted)
VALUES (
  REPLACE(UUID(), '-', ''),
  'sms_login_enabled',
  'false',
  'boolean',
  'login',
  '短信登录',
  '是否启用手机号+短信验证码登录方式',
  1,
  1,
  NOW(),
  NOW(),
  0
);
  • Step 3: 验证数据
SELECT * FROM t_system_config WHERE is_deleted = 0;

Expected: 一行数据,config_key = 'sms_login_enabled'config_value = 'false'


Task 2: 后端 Entity — SystemConfig.java

Files:

  • Create: server/src/main/java/com/emotion/entity/SystemConfig.java

  • Step 1: 创建 SystemConfig 实体

package com.emotion.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.emotion.common.BaseEntity;
import lombok.experimental.SuperBuilder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 系统配置实体
 *
 * @author system
 * @date 2026-06-27
 */
@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_system_config")
public class SystemConfig extends BaseEntity {

    /**
     * 配置唯一标识
     */
    @TableField("config_key")
    private String configKey;

    /**
     * 配置值
     */
    @TableField("config_value")
    private String configValue;

    /**
     * 值类型: boolean/string/number/json
     */
    @TableField("value_type")
    private String valueType;

    /**
     * 配置分组: system/login/notification
     */
    @TableField("config_group")
    private String configGroup;

    /**
     * 显示名称
     */
    @TableField("config_name")
    private String configName;

    /**
     * 配置说明
     */
    @TableField("description")
    private String description;

    /**
     * 排序号
     */
    @TableField("sort_order")
    private Integer sortOrder;

    /**
     * 是否在管理后台可见: 0-隐藏, 1-显示
     */
    @TableField("is_visible")
    private Integer isVisible;
}
  • Step 2: Commit
git add server/src/main/java/com/emotion/entity/SystemConfig.java
git commit -m "feat:新增系统配置实体类"

Task 3: 后端 Mapper — SystemConfigMapper.java

Files:

  • Create: server/src/main/java/com/emotion/mapper/SystemConfigMapper.java

  • Step 1: 创建 SystemConfigMapper

package com.emotion.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.emotion.entity.SystemConfig;
import org.apache.ibatis.annotations.Mapper;

/**
 * 系统配置Mapper接口
 *
 * @author system
 * @date 2026-06-27
 */
@Mapper
public interface SystemConfigMapper extends BaseMapper<SystemConfig> {
}
  • Step 2: Commit
git add server/src/main/java/com/emotion/mapper/SystemConfigMapper.java
git commit -m "feat:新增系统配置Mapper"

Task 4: 后端 DTOs

Files:

  • Create: server/src/main/java/com/emotion/dto/request/SystemConfigUpdateRequest.java

  • Create: server/src/main/java/com/emotion/dto/response/SystemConfigResponse.java

  • Create: server/src/main/java/com/emotion/dto/response/LoginConfigResponse.java

  • Step 1: 创建 SystemConfigUpdateRequest

package com.emotion.dto.request;

import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * 系统配置更新请求
 *
 * @author system
 * @date 2026-06-27
 */
@Data
public class SystemConfigUpdateRequest {

    /**
     * 配置唯一标识
     */
    @NotBlank(message = "配置标识不能为空")
    private String configKey;

    /**
     * 配置值
     */
    @NotBlank(message = "配置值不能为空")
    private String configValue;
}
  • Step 2: 创建 SystemConfigResponse
package com.emotion.dto.response;

import lombok.Data;

/**
 * 系统配置响应
 *
 * @author system
 * @date 2026-06-27
 */
@Data
public class SystemConfigResponse {

    /**
     * 配置唯一标识
     */
    private String configKey;

    /**
     * 配置值
     */
    private String configValue;

    /**
     * 值类型: boolean/string/number/json
     */
    private String valueType;

    /**
     * 配置分组
     */
    private String configGroup;

    /**
     * 显示名称
     */
    private String configName;

    /**
     * 配置说明
     */
    private String description;

    /**
     * 排序号
     */
    private Integer sortOrder;
}
  • Step 3: 创建 LoginConfigResponse
package com.emotion.dto.response;

import lombok.Data;

/**
 * 登录方式配置响应
 * 返回给小程序,告知当前启用的登录方式
 *
 * @author system
 * @date 2026-06-27
 */
@Data
public class LoginConfigResponse {

    /**
     * 是否启用微信登录
     */
    private Boolean wechatLoginEnabled;

    /**
     * 是否启用短信登录
     */
    private Boolean smsLoginEnabled;
}
  • Step 4: Commit
git add server/src/main/java/com/emotion/dto/request/SystemConfigUpdateRequest.java
git add server/src/main/java/com/emotion/dto/response/SystemConfigResponse.java
git add server/src/main/java/com/emotion/dto/response/LoginConfigResponse.java
git commit -m "feat:新增系统配置相关DTO"

Task 5: 后端 Service 接口 — SystemConfigService.java

Files:

  • Create: server/src/main/java/com/emotion/service/SystemConfigService.java

  • Step 1: 创建 SystemConfigService 接口

package com.emotion.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.dto.request.SystemConfigUpdateRequest;
import com.emotion.dto.response.LoginConfigResponse;
import com.emotion.dto.response.SystemConfigResponse;
import com.emotion.entity.SystemConfig;

import java.util.List;

/**
 * 系统配置服务接口
 *
 * @author system
 * @date 2026-06-27
 */
public interface SystemConfigService extends IService<SystemConfig> {

    /**
     * 获取所有可见配置列表
     */
    List<SystemConfigResponse> getVisibleConfigList();

    /**
     * 批量更新配置
     */
    void batchUpdateConfigs(List<SystemConfigUpdateRequest> requests);

    /**
     * 获取配置值(带 Redis 缓存)
     *
     * @param configKey 配置标识
     * @return 配置值字符串,不存在返回 null
     */
    String getConfigValue(String configKey);

    /**
     * 获取登录方式配置
     */
    LoginConfigResponse getLoginConfig();
}
  • Step 2: Commit
git add server/src/main/java/com/emotion/service/SystemConfigService.java
git commit -m "feat:新增系统配置服务接口"

Task 6: 后端 Service 实现 — SystemConfigServiceImpl.java

Files:

  • Create: server/src/main/java/com/emotion/service/impl/SystemConfigServiceImpl.java

  • Step 1: 创建 SystemConfigServiceImpl

package com.emotion.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.dto.request.SystemConfigUpdateRequest;
import com.emotion.dto.response.LoginConfigResponse;
import com.emotion.dto.response.SystemConfigResponse;
import com.emotion.entity.SystemConfig;
import com.emotion.mapper.SystemConfigMapper;
import com.emotion.service.SystemConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 系统配置服务实现类
 *
 * @author system
 * @date 2026-06-27
 */
@Slf4j
@Service
public class SystemConfigServiceImpl extends ServiceImpl<SystemConfigMapper, SystemConfig> implements SystemConfigService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String CACHE_PREFIX = "system_config:";
    private static final long CACHE_EXPIRE_HOURS = 24;
    private static final String SMS_LOGIN_ENABLED_KEY = "sms_login_enabled";

    @Override
    public List<SystemConfigResponse> getVisibleConfigList() {
        LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SystemConfig::getIsVisible, 1)
               .orderByAsc(SystemConfig::getSortOrder);
        List<SystemConfig> configs = list(wrapper);
        return configs.stream().map(this::convertToResponse).collect(Collectors.toList());
    }

    @Override
    public void batchUpdateConfigs(List<SystemConfigUpdateRequest> requests) {
        for (SystemConfigUpdateRequest request : requests) {
            LambdaUpdateWrapper<SystemConfig> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(SystemConfig::getConfigKey, request.getConfigKey())
                         .set(SystemConfig::getConfigValue, request.getConfigValue());
            update(updateWrapper);

            // 清除缓存
            redisTemplate.delete(CACHE_PREFIX + request.getConfigKey());
            log.info("更新系统配置: key={}, value={}", request.getConfigKey(), request.getConfigValue());
        }
    }

    @Override
    public String getConfigValue(String configKey) {
        // 先查 Redis 缓存
        String cacheKey = CACHE_PREFIX + configKey;
        String cached = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
            return cached;
        }

        // 查数据库
        LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SystemConfig::getConfigKey, configKey);
        SystemConfig config = getOne(wrapper);
        if (config == null) {
            return null;
        }

        // 写入缓存
        redisTemplate.opsForValue().set(cacheKey, config.getConfigValue(), CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
        return config.getConfigValue();
    }

    @Override
    public LoginConfigResponse getLoginConfig() {
        LoginConfigResponse response = new LoginConfigResponse();
        response.setWechatLoginEnabled(true);

        String smsEnabled = getConfigValue(SMS_LOGIN_ENABLED_KEY);
        response.setSmsLoginEnabled("true".equalsIgnoreCase(smsEnabled));

        return response;
    }

    /**
     * 实体转响应对象
     */
    private SystemConfigResponse convertToResponse(SystemConfig config) {
        SystemConfigResponse response = new SystemConfigResponse();
        response.setConfigKey(config.getConfigKey());
        response.setConfigValue(config.getConfigValue());
        response.setValueType(config.getValueType());
        response.setConfigGroup(config.getConfigGroup());
        response.setConfigName(config.getConfigName());
        response.setDescription(config.getDescription());
        response.setSortOrder(config.getSortOrder());
        return response;
    }
}
  • Step 2: Commit
git add server/src/main/java/com/emotion/service/impl/SystemConfigServiceImpl.java
git commit -m "feat:新增系统配置服务实现(含Redis缓存)"

Task 7: 后端管理端 Controller — SystemConfigController.java

Files:

  • Create: server/src/main/java/com/emotion/controller/SystemConfigController.java

  • Step 1: 创建 SystemConfigController

此 controller 的路径为 /admin/systemConfig/**,自动被 AdminAuthInterceptor 保护,不需要登录用户 JWT。

package com.emotion.controller;

import com.emotion.common.Result;
import com.emotion.dto.request.SystemConfigUpdateRequest;
import com.emotion.dto.response.SystemConfigResponse;
import com.emotion.service.SystemConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 系统配置管理控制器(管理端)
 *
 * @author system
 * @date 2026-06-27
 */
@RestController
@RequestMapping("/admin/systemConfig")
@Tag(name = "系统配置管理", description = "系统配置的查询和更新功能")
public class SystemConfigController {

    @Autowired
    private SystemConfigService systemConfigService;

    /**
     * 获取所有可见配置列表
     */
    @GetMapping("/list")
    @Operation(summary = "获取配置列表", description = "获取所有可见的系统配置列表")
    public Result<List<SystemConfigResponse>> list() {
        List<SystemConfigResponse> configs = systemConfigService.getVisibleConfigList();
        return Result.success(configs);
    }

    /**
     * 批量更新配置
     */
    @PutMapping("/update")
    @Operation(summary = "批量更新配置", description = "批量更新系统配置值")
    public Result<Void> update(@RequestBody @Validated List<SystemConfigUpdateRequest> requests) {
        systemConfigService.batchUpdateConfigs(requests);
        return Result.success("更新成功", null);
    }
}
  • Step 2: Commit
git add server/src/main/java/com/emotion/controller/SystemConfigController.java
git commit -m "feat:新增系统配置管理端Controller"

Task 8: 后端公开接口 — AuthController 新增 loginConfig

Files:

  • Modify: server/src/main/java/com/emotion/controller/AuthController.java

  • Step 1: 在 AuthController 中添加 loginConfig 接口

AuthController.java 中添加 SystemService 注入和新接口方法。

添加 import

import com.emotion.dto.response.LoginConfigResponse;

添加 service 注入(在已有的 @Autowired 字段后面):

@Autowired
private SystemConfigService systemConfigService;

添加新接口方法(放在 wechatLogin 方法后面):

/**
 * 获取登录方式配置
 */
@GetMapping("/loginConfig")
@Operation(summary = "获取登录方式配置", description = "返回当前启用的登录方式,供小程序登录页动态渲染")
public Result<LoginConfigResponse> getLoginConfig() {
    LoginConfigResponse response = systemConfigService.getLoginConfig();
    return Result.success(response);
}
  • Step 2: 在 WebMvcConfig 白名单中放行

修改 server/src/main/java/com/emotion/config/WebMvcConfig.java,在 JWT 拦截器的 excludePathPatterns 中添加一行:

"/auth/loginConfig", // 登录方式配置接口(免登录)

添加位置:在 "/auth/wechat/login" 行的上方或下方。

  • Step 3: Commit
git add server/src/main/java/com/emotion/controller/AuthController.java
git add server/src/main/java/com/emotion/config/WebMvcConfig.java
git commit -m "feat:新增公开接口 /auth/loginConfig 并放行白名单"

Task 9: 后端编译验证

  • Step 1: 编译后端
cd server
mvn clean install -DskipTests

Expected: BUILD SUCCESS

  • Step 2: 如果有编译错误,修复后重新编译直到通过

  • Step 3: Commit(如果有修复)

git add -A
git commit -m "fix:修复系统配置编译错误"

Task 10: 管理后台 — API 和路由配置

Files:

  • Create: web-admin/src/api/systemConfig.ts

  • Modify: web-admin/src/router/index.ts

  • Modify: web-admin/src/config/menu.ts

  • Step 1: 创建 systemConfig API 文件

web-admin/src/api/systemConfig.ts

import request from '@/utils/request'

// 获取所有可见配置列表
export function getSystemConfigList() {
  return request({
    url: '/admin/systemConfig/list',
    method: 'get'
  })
}

// 批量更新配置
export function updateSystemConfig(data: { configKey: string; configValue: string }[]) {
  return request({
    url: '/admin/systemConfig/update',
    method: 'put',
    data
  })
}
  • Step 2: 添加路由配置

web-admin/src/router/index.ts 中,在 dictionary 路由块之前添加:

{
  path: '/system',
  component: Layout,
  redirect: '/system/settings',
  meta: { title: '系统设置', icon: 'Setting' },
  children: [
    {
      path: 'settings',
      name: 'SystemSettings',
      component: () => import('@/views/system/SystemSettings.vue'),
      meta: { title: '基础设置' }
    }
  ]
},
  • Step 3: 添加菜单配置

web-admin/src/config/menu.tsmenuConfig 数组中,在 AI配置管理 项之前添加:

{
  path: '/system',
  title: '系统设置',
  icon: 'Setting'
},
  • Step 4: Commit
git add web-admin/src/api/systemConfig.ts
git add web-admin/src/router/index.ts
git add web-admin/src/config/menu.ts
git commit -m "feat:管理后台新增系统设置路由和菜单"

Task 11: 管理后台 — SystemSettings.vue 页面

Files:

  • Create: web-admin/src/views/system/SystemSettings.vue

  • Step 1: 创建 SystemSettings 页面

<template>
  <div class="system-settings">
    <div class="page-header">
      <h2>基础设置</h2>
      <el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
    </div>

    <el-card v-if="loading" v-loading="true" class="loading-card" />

    <div v-else class="config-groups">
      <el-card v-for="(items, group) in groupedConfigs" :key="group" class="config-group">
        <template #header>
          <span class="group-title">{{ groupLabels[group] || group }}</span>
        </template>

        <div v-for="item in items" :key="item.configKey" class="config-item">
          <div class="config-label">
            <span class="config-name">{{ item.configName }}</span>
            <span class="config-desc">{{ item.description }}</span>
          </div>
          <div class="config-control">
            <el-switch
              v-if="item.valueType === 'boolean'"
              v-model="editValues[item.configKey]"
              :active-value="'true'"
              :inactive-value="'false'"
            />
            <el-input
              v-else-if="item.valueType === 'string'"
              v-model="editValues[item.configKey]"
              style="width: 300px"
            />
            <el-input-number
              v-else-if="item.valueType === 'number'"
              v-model="editValues[item.configKey]"
              style="width: 200px"
            />
            <span v-else>{{ editValues[item.configKey] }}</span>
          </div>
        </div>
      </el-card>

      <el-empty v-if="Object.keys(groupedConfigs).length === 0" description="暂无配置项" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { getSystemConfigList, updateSystemConfig } from '@/api/systemConfig'

interface ConfigItem {
  configKey: string
  configValue: string
  valueType: string
  configGroup: string
  configName: string
  description: string
  sortOrder: number
}

const loading = ref(false)
const saving = ref(false)
const configs = ref<ConfigItem[]>([])
const editValues = ref<Record<string, string | boolean>>({})

const groupLabels: Record<string, string> = {
  system: '系统配置',
  login: '登录设置',
  notification: '通知设置'
}

const groupedConfigs = computed(() => {
  const groups: Record<string, ConfigItem[]> = {}
  for (const item of configs.value) {
    if (!groups[item.configGroup]) {
      groups[item.configGroup] = []
    }
    groups[item.configGroup].push(item)
  }
  return groups
})

const fetchConfigs = async () => {
  loading.value = true
  try {
    const res = await getSystemConfigList()
    configs.value = res.data || []
    // 初始化编辑值
    const values: Record<string, string | boolean> = {}
    for (const item of configs.value) {
      if (item.valueType === 'boolean') {
        values[item.configKey] = item.configValue
      } else {
        values[item.configKey] = item.configValue
      }
    }
    editValues.value = values
  } catch (error) {
    ElMessage.error('获取配置列表失败')
  } finally {
    loading.value = false
  }
}

const handleSave = async () => {
  saving.value = true
  try {
    const updates = configs.value.map(item => ({
      configKey: item.configKey,
      configValue: String(editValues.value[item.configKey])
    }))
    await updateSystemConfig(updates)
    ElMessage.success('保存成功')
    await fetchConfigs()
  } catch (error) {
    ElMessage.error('保存失败')
  } finally {
    saving.value = false
  }
}

onMounted(() => {
  fetchConfigs()
})
</script>

<style scoped>
.system-settings {
  padding: 20px;
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.page-header h2 {
  margin: 0;
  font-size: 20px;
}

.loading-card {
  height: 200px;
}

.config-group {
  margin-bottom: 20px;
}

.group-title {
  font-weight: 600;
  font-size: 16px;
}

.config-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 0;
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.config-item:last-child {
  border-bottom: none;
}

.config-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.config-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--el-text-color-primary);
}

.config-desc {
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
</style>
  • Step 2: Commit
git add web-admin/src/views/system/SystemSettings.vue
git commit -m "feat:管理后台新增系统设置页面"

Task 12: 小程序 — 新增 getLoginConfig 接口

Files:

  • Modify: mini-program/src/services/auth.js

  • Step 1: 在 auth.js 中添加 getLoginConfig 方法

mini-program/src/services/auth.jsgetSmsCode 方法之前,添加:

/**
 * 获取登录方式配置
 * @returns {Promise<Object>} 登录方式配置
 */
export const getLoginConfig = async () => {
  const response = await get('/auth/loginConfig')
  return response
}
  • Step 2: 在底部的 default 导出对象中添加 getLoginConfig
export default {
  getLoginConfig,
  getSmsCode,
  login,
  wechatLogin,
  logout,
  refreshToken,
  validateToken,
  validateTokenStrict,
  getCurrentUserInfo,
  checkPhone
}
  • Step 3: Commit
git add mini-program/src/services/auth.js
git commit -m "feat:小程序新增获取登录方式配置接口"

Task 13: 小程序 — 登录页条件渲染

Files:

  • Modify: mini-program/src/pages/login/index.vue

  • Step 1: 修改 template — 用 v-if 控制 SMS 相关元素

mini-program/src/pages/login/index.vue 中:

  1. 在「微信一键登录」按钮的 </view> 之后,「分隔线」的 <view class="login-divider"> 上添加 v-if

将:

<view class="login-divider">
  <view></view>
  <text>或使用手机号登录</text>
  <view></view>
</view>

改为:

<view class="login-divider" v-if="loginConfig.smsLoginEnabled">
  <view></view>
  <text>或使用手机号登录</text>
  <view></view>
</view>
  1. <view class="form"> 上添加 v-if

将:

<view class="form">

改为:

<view class="form" v-if="loginConfig.smsLoginEnabled">
  1. 在「开启旅程」按钮上添加 v-if

将:

<view
  class="btn-primary login-btn"
  :class="{ disabled: !canSubmit || loading }"
  @click="handleLogin"
>

改为:

<view
  v-if="loginConfig.smsLoginEnabled"
  class="btn-primary login-btn"
  :class="{ disabled: !canSubmit || loading }"
  @click="handleLogin"
>
  • Step 2: 修改 script — 添加 loginConfig 状态和接口调用
  1. 添加 import:在现有的 import { getSmsCode } from '../../services/auth.js' 行,改为:
import { getSmsCode, getLoginConfig } from '../../services/auth.js'
  1. 添加 loginConfig ref:在 const isCountingDown = ref(false) 行之后添加:
const loginConfig = ref({ wechatLoginEnabled: true, smsLoginEnabled: false })
  1. onMounted 回调中添加获取登录配置的逻辑,将现有的 onMounted 改为:
onMounted(async () => {
  const windowInfo = uni.getWindowInfo()
  statusBarHeight.value = windowInfo.statusBarHeight || 20
  safeAreaTop.value = windowInfo.safeAreaInsets?.top || windowInfo.statusBarHeight || 20
  safeAreaBottom.value = windowInfo.safeAreaInsets?.bottom || 0

  try {
    const res = await getLoginConfig()
    if (res.data) {
      loginConfig.value = res.data
    }
  } catch (e) {
    // 接口失败时默认只显示微信登录
    loginConfig.value = { wechatLoginEnabled: true, smsLoginEnabled: false }
  }
})
  • Step 3: Commit
git add mini-program/src/pages/login/index.vue
git commit -m "feat:小程序登录页根据配置动态显示短信登录入口"

Task 14: 部署后端到服务器

  • Step 1: 部署后端
cd G:\IdeaProjects\emotion-museun
python deploy.py backend

Expected: 部署成功,无报错

  • Step 2: 验证后端接口

用 curl 或浏览器验证:

curl https://lifescript.happylifeos.com/api/auth/loginConfig

Expected 响应(短信默认关闭):

{"code":200,"message":"操作成功","data":{"wechatLoginEnabled":true,"smsLoginEnabled":false},"timestamp":...}

Task 15: 管理后台验收

  • Step 1: 打开管理后台

访问 https://lifescript.happylifeos.com/emotion-museum-admin/

  • Step 2: 验证侧边栏出现「系统设置」菜单

  • Step 3: 点击进入「基础设置」页面,确认显示「短信登录」开关

  • Step 4: 确认开关默认为关闭状态

  • Step 5: 打开开关并点击「保存」,确认保存成功

  • Step 6: 再次调用 loginConfig 接口验证值已更新

curl https://lifescript.happylifeos.com/api/auth/loginConfig

Expected: smsLoginEnabled 变为 true

  • Step 7: 将开关改回关闭状态(为小程序验收做准备)

Task 16: 小程序 H5 验收

  • Step 1: 启动小程序 H5 开发服务器
cd mini-program
npm run dev:h5
  • Step 2: 浏览器打开 http://localhost:5284

  • Step 3: 验证登录页只显示「微信一键登录」按钮,无手机号表单

  • Step 4: 在管理后台打开短信登录开关

  • Step 5: 刷新小程序 H5 登录页,验证出现手机号+验证码表单

  • Step 6: 关闭开关,刷新登录页,验证手机号表单消失

  • Step 7: 确认浏览器 Console 无任何报错

  • Step 8: 将开关恢复为关闭状态


Task 17: 最终提交

  • Step 1: 确认所有代码已提交
git status

Expected: working tree clean

  • Step 2: 如果没有全部提交,补充提交
git add -A
git commit -m "feat:短信登录开关与通用系统配置功能完成"