gatsby преди 3 седмици
родител
ревизия
9106710647

+ 37 - 1
backend/app/api/v1/module_system/auth/controller.py

@@ -19,7 +19,9 @@ from .schema import (
     JWTOutSchema,
     LoginMiniRequestSchema,
     LogoutPayloadSchema,
-    RefreshTokenPayloadSchema, SmsCodeSchema,
+    RefreshTokenPayloadSchema, 
+    SmsCodeSchema,
+    SmsLoginRequestSchema,
 )
 from .service import AutoLoginService, CaptchaService, LoginService, SmsCodeService
 
@@ -73,6 +75,40 @@ async def login_mini_controller(
     return SuccessResponse(data=login_token.model_dump(), msg="登录成功")
 
 
+@AuthRouter.post(
+    "/login/sms",
+    summary="短信登录",
+    description="使用手机号和验证码登录",
+    response_model=JWTOutSchema,
+)
+async def login_sms_controller(
+    request: Request,
+    login_form: SmsLoginRequestSchema,
+    redis: Annotated[Redis, Depends(redis_getter)],
+    db: Annotated[AsyncSession, Depends(db_getter)],
+) -> JSONResponse:
+    """
+    短信登录
+
+    参数:
+    - request (Request): FastAPI请求对象
+    - login_form (SmsLoginRequestSchema): 短信登录表单(手机号和验证码)
+    - redis (Redis): Redis客户端对象
+    - db (AsyncSession): 数据库会话对象
+
+    返回:
+    - JWTOutSchema: 包含访问令牌和刷新令牌的响应模型
+
+    异常:
+    - CustomException: 认证失败时抛出异常。
+    """
+    login_token = await LoginService.authenticate_sms_user_service(
+        request=request, redis=redis, login_form=login_form, db=db
+    )
+    log.info(f"用户{login_form.mobile}短信登录成功")
+    return SuccessResponse(data=login_token.model_dump(), msg="登录成功")
+
+
 @AuthRouter.post(
     "/login",
     summary="登录",

+ 10 - 2
backend/app/api/v1/module_system/auth/schema.py

@@ -6,15 +6,18 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator, field_valida
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from app.api.v1.module_system.user.model import UserModel
+from app.utils.sms_util import SmsTemplateEnum
+
 
 class SmsCodeSchema(BaseModel):
     # 验证手机号格式
     mobile: str = Field(..., description="手机号")
     # 短信模版
-    template_code: str = Field(..., description="短信模版")
-    # 验证码
+    template_name: str = Field(..., description="短信模版")
+    # # 验证码
     code: Optional[str] = Field(default=None, description="验证码")
 
+
     @field_validator("mobile", mode="before")
     @classmethod
     def validate_mobile(cls, v: str) -> str:
@@ -129,3 +132,8 @@ class LoginMiniRequestSchema(BaseModel):
     username: str = Field(..., min_length=1, description="用户名")
     password: str = Field(..., min_length=1, description="密码")
     login_type: Optional[str] = Field(default="mini", description="登录类型")
+
+
+class SmsLoginRequestSchema(SmsCodeSchema):
+
+    code: str = Field(..., description="验证码")

+ 84 - 6
backend/app/api/v1/module_system/auth/service.py

@@ -22,9 +22,10 @@ from app.core.security import (
     decode_access_token,
 )
 from app.utils.captcha_util import CaptchaUtil
-from app.utils.common_util import get_random_character
+from app.utils.common_util import get_random_character, generate_random_code
 from app.utils.hash_bcrpy_util import PwdUtil
 from app.utils.ip_local_util import IpLocalUtil
+from app.utils.sms_util import SendSmsRequest, SmsTemplateEnum, SmsSender
 
 from .schema import (
     AuthSchema,
@@ -35,7 +36,9 @@ from .schema import (
     JWTPayloadSchema,
     LoginMiniRequestSchema,
     LogoutPayloadSchema,
-    RefreshTokenPayloadSchema, SmsCodeSchema,
+    RefreshTokenPayloadSchema, 
+    SmsCodeSchema,
+    SmsLoginRequestSchema,
 )
 
 CaptchaKey = NewType("CaptchaKey", str)
@@ -155,6 +158,62 @@ class LoginService:
 
         return token
 
+    @classmethod
+    async def authenticate_sms_user_service(
+        cls,
+        request: Request,
+        redis: Redis,
+        login_form: SmsLoginRequestSchema,
+        db: AsyncSession,
+    ) -> JWTOutSchema:
+        """
+        短信验证码登录
+
+        参数:
+        - request (Request): FastAPI请求对象
+        - redis (Redis): Redis客户端对象
+        - login_form (SmsLoginRequestSchema): 短信登录表单数据
+        - db (AsyncSession): 数据库会话对象
+
+        返回:
+        - JWTOutSchema: 包含访问令牌和刷新令牌的响应模型
+
+        异常:
+        - CustomException: 认证失败时抛出异常。
+        """
+        mobile = login_form.mobile
+        code = login_form.code
+
+        # 验证验证码
+        verify_result = await SmsCodeService.verify_sms_code_service(login_form, redis)
+        if not verify_result:
+            raise CustomException(msg="验证码已过期或错误")
+
+        # 根据手机号查找用户
+        auth = AuthSchema(db=db)
+        user = await UserCRUD(auth).get_by_mobile_crud(mobile=mobile)
+
+        if not user:
+            raise CustomException(msg="用户不存在")
+
+        if user.status == "1":
+            raise CustomException(msg="用户已被停用")
+
+        # 更新最后登录时间
+        user = await UserCRUD(auth).update_last_login_crud(id=user.id)
+        if not user:
+            raise CustomException(msg="用户不存在")
+
+        # 创建token
+        token = await cls.create_token_service(
+            request=request,
+            redis=redis,
+            user=user,
+            login_type="sms",
+        )
+
+        return token
+
     @classmethod
     async def create_token_service(
         cls, request: Request, redis: Redis, user: UserModel, login_type: str
@@ -610,12 +669,12 @@ class AutoLoginService:
 class SmsCodeService:
     """短信验证码服务"""
     SMS_CODE_PREFIX = "sms_code"
-    SMS_CODE_EXPIRE = 60 * 5
+    SMS_CODE_EXPIRE = 60 * 10
     
     @classmethod
     async def send_sms_code_service(
         cls, sms_code: SmsCodeSchema, redis: Redis
-    ) -> None:
+    ) -> bool:
         """
         发送短信验证码
 
@@ -626,7 +685,25 @@ class SmsCodeService:
         异常:
         - CustomException: 验证码发送失败时抛出异常。
         """
-        pass
+        template = SmsTemplateEnum.get_template_by_name(sms_code.template_name)
+
+        code = generate_random_code(6)
+
+        redis_key = f"{cls.SMS_CODE_PREFIX}:{sms_code.template_name}:{sms_code.mobile}"
+        await RedisCURD(redis).set(
+            key=redis_key,
+            value=code,
+            expire=cls.SMS_CODE_EXPIRE,
+        )
+
+        request = SendSmsRequest(
+            phone_numbers=sms_code.mobile,
+            template_code=template.template_code,
+            template_param=template.template_param_fn(code=code),
+        )
+
+        return await SmsSender.send_sms(request)
+        
     
     @classmethod
     async def verify_sms_code_service(
@@ -645,8 +722,9 @@ class SmsCodeService:
         异常:
         - CustomException: 验证码验证失败时抛出异常。
         """
-        redis_key = f"{cls.SMS_CODE_PREFIX}:{sms_code.mobile}"
+        redis_key = f"{cls.SMS_CODE_PREFIX}:{sms_code.template_name}:{sms_code.mobile}"
         code = await RedisCURD(redis).get(redis_key)
         if not code or code != sms_code.code:
             return False
+        await RedisCURD(redis).delete(redis_key)
         return True

+ 12 - 10
backend/app/api/v1/module_system/tenant/service.py

@@ -49,8 +49,8 @@ class TenantService:
         )
 
     @classmethod
-    async def create_service(cls, auth: AuthSchema, data: TenantCreateSchema) -> dict:
-        ensure_platform_tenant_management(auth)
+    async def create_service(cls, auth: AuthSchema, data: TenantCreateSchema, password: str = None) -> dict:
+        #ensure_platform_tenant_management(auth)
         if await TenantCRUD(auth).get(name=data.name):
             raise CustomException(msg="创建失败,名称已存在")
         if await TenantCRUD(auth).get(code=data.code):
@@ -59,26 +59,28 @@ class TenantService:
         tenant_obj = await TenantCRUD(auth).create_crud(data=data)
         if not tenant_obj:
             raise CustomException(msg="创建租户失败")
-        log.info(f"创建租户成功: {tenant_obj.name},编码: {tenant_obj.code}")
+        log.info(f"创建租户成功: {tenant_obj.name},编码: {tenant_obj.code}, ID: {tenant_obj.id}")
         # 创建初始管理员
-        await cls._create_tenant_admin_user(auth, tenant_obj)
+        await cls._create_tenant_admin_user(auth, tenant_obj, password=password)
         # 创建积分账户
         await PointsService.create_points_service(auth, PointsCreateSchema(tenant_id=tenant_obj.id, points=Decimal("0.00")))
         await auth.db.refresh(tenant_obj)
         return TenantOutSchema.model_validate(tenant_obj).model_dump()
 
     @classmethod
-    async def _create_tenant_admin_user(cls, auth: AuthSchema, tenant_obj: TenantModel) -> None:
+    async def _create_tenant_admin_user(cls, auth: AuthSchema, tenant_obj: TenantModel, password: str = None) -> None:
         username = f"{tenant_obj.code}"
         if await UserCRUD(auth).get_by_username_crud(username=username):
             raise CustomException(msg=f"初始管理员用户名已存在: {username},请更换租户编码后重试")
 
-        password_length = 12
-        characters = string.ascii_letters + string.digits + "!@#$%^&*"
-        password = "".join(random.choice(characters) for _ in range(password_length))
+        # password_length = 12
+        # characters = string.ascii_letters + string.digits + "!@#$%^&*"
+        # password = "".join(random.choice(characters) for _ in range(password_length))
+        password = password or tenant_obj.code
         admin_data = {
             "username": username,
-            "password": PwdUtil.set_password_hash(password=tenant_obj.code),
+            "mobile": tenant_obj.code,
+            "password": PwdUtil.set_password_hash(password=password),
             "name": f"{tenant_obj.name}管理员",
             "tenant_id": tenant_obj.id,
             "status": "0",
@@ -97,7 +99,7 @@ class TenantService:
             raise CustomException(msg="创建租户初始管理员失败")
 
         log.info(
-            f"为租户[{tenant_obj.name}]创建初始管理员成功,用户名: {username},临时密码: {password}"
+            f"为租户[{tenant_obj.name}]创建初始管理员成功,用户名: {username}"
         )
 
     @classmethod

+ 4 - 2
backend/app/api/v1/module_system/user/controller.py

@@ -3,13 +3,14 @@ from typing import Annotated
 
 from fastapi import APIRouter, Body, Depends, Path, Request, UploadFile
 from fastapi.responses import JSONResponse, StreamingResponse
+from redis.asyncio.client import Redis
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from app.api.v1.module_system.auth.schema import AuthSchema
 from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
 from app.core.base_params import PaginationQueryParam
 from app.core.base_schema import BatchSetAvailable
-from app.core.dependencies import AuthPermission, db_getter, get_current_user
+from app.core.dependencies import AuthPermission, db_getter, get_current_user, redis_getter
 from app.core.logger import log
 from app.core.router_class import OperationLogRoute
 from app.utils.common_util import bytes2file_response
@@ -158,6 +159,7 @@ async def reset_password_controller(
 )
 async def register_user_controller(
     data: UserRegisterSchema,
+    redis: Annotated[Redis, Depends(redis_getter)],
     db: Annotated[AsyncSession, Depends(db_getter)],
 ) -> JSONResponse:
     """
@@ -171,7 +173,7 @@ async def register_user_controller(
     - JSONResponse: 注册用户JSON响应
     """
     auth = AuthSchema(db=db)
-    user_register_result = await UserService.register_user_service(data=data, auth=auth)
+    user_register_result = await UserService.register_user_service(data=data, auth=auth, redis=redis)
     log.info(f"{data.username} 注册用户成功: {user_register_result}")
     return SuccessResponse(data=user_register_result, msg="注册用户成功")
 

+ 9 - 5
backend/app/api/v1/module_system/user/schema.py

@@ -1,3 +1,4 @@
+from typing import Optional
 from urllib.parse import urlparse
 
 from fastapi import Query
@@ -101,11 +102,14 @@ class CurrentUserUpdateSchema(BaseModel):
         return self
 
 
-class UserRegisterSchema(SmsCodeSchema):
+class UserRegisterSchema(BaseModel):
     """注册"""
 
     name: str | None = Field(default=None, description="名称")
+    template_name: Optional[str] = Field(default="verify", description="模版名称")
+    invite_code: str = Field(default=None, description="邀请码")
     mobile: str = Field(default=None, description="手机号")
+    sms_code: Optional[str] = Field(default=None, description="验证码")
     username: str = Field(..., description="账号")
     password: str = Field(..., description="密码哈希值")
     role_ids: list[int] | None = Field(default=[1], description="角色ID")
@@ -148,10 +152,10 @@ class UserRegisterSchema(SmsCodeSchema):
         if not v:
             raise ValueError("账号不能为空")
         # 字母开头,允许字母数字_.-
-        import re
-
-        if not re.match(r"^[A-Za-z][A-Za-z0-9_.-]{2,31}$", v):
-            raise ValueError("账号需字母开头,3-32位,仅含字母/数字/_ . -")
+        # import re
+        #
+        # if not re.match(r"^[A-Za-z][A-Za-z0-9_.-]{2,31}$", v):
+        #     raise ValueError("账号需字母开头,3-32位,仅含字母/数字/_ . -")
         return v
 
     @model_validator(mode="after")

+ 26 - 8
backend/app/api/v1/module_system/user/service.py

@@ -4,7 +4,7 @@ from typing import Any
 import pandas as pd
 from fastapi import UploadFile
 
-from app.api.v1.module_system.auth.schema import AuthSchema
+from app.api.v1.module_system.auth.schema import AuthSchema, SmsCodeSchema
 from app.api.v1.module_system.dept.crud import DeptCRUD
 from app.api.v1.module_system.menu.crud import MenuCRUD
 from app.api.v1.module_system.menu.schema import MenuOutSchema
@@ -17,6 +17,7 @@ from app.utils.common_util import traversal_to_tree
 from app.utils.excel_util import ExcelUtil
 from app.utils.hash_bcrpy_util import PwdUtil
 from app.utils.upload_util import UploadUtil
+from redis.asyncio.client import Redis
 
 from .crud import UserCRUD
 from .schema import (
@@ -30,6 +31,7 @@ from .schema import (
     UserRegisterSchema,
     UserUpdateSchema,
 )
+from ..auth.service import SmsCodeService
 from ..tenant.schema import TenantCreateSchema
 from ..tenant.service import TenantService
 
@@ -474,7 +476,7 @@ class UserService:
         return UserOutSchema.model_validate(new_user).model_dump()
 
     @classmethod
-    async def register_user_service(cls, auth: AuthSchema, data: UserRegisterSchema) -> dict:
+    async def register_user_service(cls, auth: AuthSchema, redis: Redis, data: UserRegisterSchema) -> dict:
         """
         用户注册
 
@@ -485,12 +487,33 @@ class UserService:
         返回:
         - Dict: 注册后的用户详情字典
         """
+        if not data.invite_code or data.invite_code != "8888":
+            raise CustomException("无效邀请码")
+
         # 检查用户名是否存在
         data.username = data.mobile
-        username_ok = await UserCRUD(auth).get_by_username_crud(username=data.username)
+        username_ok = await UserCRUD(auth).get_by_mobile_crud(mobile=data.mobile)
         if username_ok:
             raise CustomException(msg="账号已存在")
 
+        verify_result = await SmsCodeService.verify_sms_code_service(
+            sms_code=SmsCodeSchema(
+                mobile=data.mobile,
+                template_name=data.template_name or "verify",
+                code=data.sms_code,
+            ),
+            redis=redis
+        )
+
+        if not verify_result:
+            raise CustomException("验证码过期或错误")
+
+        tenant_data = TenantCreateSchema(
+            name=data.mobile,
+            code=data.mobile,
+        )
+        return await TenantService.create_service(auth=auth, data=tenant_data, password=data.password,)
+
         # data.password = PwdUtil.set_password_hash(password=data.password)
         # data.name = data.username
         # create_dict = data.model_dump(exclude_unset=True, exclude={"role_ids", "position_ids"})
@@ -503,11 +526,6 @@ class UserService:
         # if data.role_ids:
         #     await UserCRUD(auth).set_user_roles_crud(user_ids=[result.id], role_ids=data.role_ids)
         # return UserOutSchema.model_validate(result).model_dump()
-        tenant_data = TenantCreateSchema(
-            name=data.mobile,
-            code=data.mobile,
-        )
-        return await TenantService.create_service(auth=auth, data=tenant_data)
 
     @classmethod
     async def forget_password_service(

+ 1 - 1
backend/app/plugin/module_payment/points/service.py

@@ -25,7 +25,7 @@ class PointsService:
         if existing_points:
             raise CustomException(msg="租户已存在积分账户")
 
-        points = await crud.create(data.model_dump(exclude_unset=True))
+        points = await crud.create(data.model_dump(exclude_unset=True), skip_tenant_id=True)
         return PointsOutSchema.model_validate(points)
 
     @classmethod

+ 14 - 0
backend/app/utils/common_util.py

@@ -77,6 +77,20 @@ def get_random_character() -> str:
     return uuid.uuid4().hex
 
 
+def generate_random_code(length: int = 6) -> str:
+    """
+    生成指定长度的随机数字验证码
+
+    参数:
+    - length (int): 验证码长度,默认6位
+
+    返回:
+    - str: 随机数字验证码字符串
+    """
+    import random
+    return ''.join(random.choices('0123456789', k=length))
+
+
 def uuid4_str() -> str:
     """
     数据库引擎 UUID 类型兼容:返回无连字符的 UUID 字符串。

+ 44 - 9
backend/app/utils/sms_util.py

@@ -1,7 +1,7 @@
 import enum
 import os
 import json
-from typing import Optional
+from typing import Optional, Dict, Any
 from collections import namedtuple
 from functools import lru_cache
 
@@ -15,15 +15,25 @@ from pydantic import BaseModel, Field, AliasChoices
 from pydantic_settings import SettingsConfigDict, BaseSettings
 
 from app.config.path_conf import ENV_DIR
+from app.core.logger import log
+from app.core.exceptions import CustomException
 
-_SmsTemplate = namedtuple("_SmsTemplate", ["template_code", "template_param_fn"])
+_SmsTemplate = namedtuple("_SmsTemplate", ["template_name", "template_code", "template_param_fn"])
 
 class SmsTemplateEnum(enum.Enum):
     VERIFICATION_CODE = _SmsTemplate(
+        template_name="verify",
         template_code="SMS_333796424",
         template_param_fn=lambda code: f'{{"code": "{code}"}}'
     )
 
+    @staticmethod
+    def get_template_by_name(template_name: str) -> "_SmsTemplate":
+        for template in SmsTemplateEnum:
+            if template.value.template_name == template_name:
+                return template.value
+        raise CustomException(f"未找到模板: template_name={ template_name}")
+
 
 class SendSmsRequest(BaseModel):
     phone_numbers: str = Field(..., description="支持向不同的手机号码发送短信,手机号码之间以半角逗号(,)分隔。"
@@ -75,16 +85,41 @@ class SmsSender:
         return Dysmsapi20170525Client(config)
 
     @classmethod
-    async def send_sms(cls, sms_request: SendSmsRequest) -> None:
+    async def send_sms(cls, sms_request: SendSmsRequest) -> bool:
+        """
+        发送短信
+        
+        参数:
+        - sms_request (SendSmsRequest): 短信发送请求对象
+        
+        返回:
+        - Dict[str, Any]: 短信发送响应结果
+        
+        异常:
+        - Exception: 发送失败时抛出异常
+        """
         client = SmsSender.get_client()
         send_sms_request = dysmsapi_20170525_models.SendSmsRequest(**sms_request.model_dump(exclude_none=True))
+        
         runtime = util_models.RuntimeOptions()
+        
+        phone_numbers = sms_request.phone_numbers
+        log.info(f"开始发送短信: 手机号={phone_numbers}, 模板={sms_request.template_code}, 参数={sms_request.template_param}")
+        
         try:
             resp = await client.send_sms_with_options_async(send_sms_request, runtime)
-            print(json.dumps(resp, default=str, indent=2))
+            # 将响应转换为字典
+            resp_dict = json.loads(json.dumps(resp, default=str))
+            # 检查发送结果
+            if hasattr(resp.body, "code") and resp.body.code == 'OK':
+                log.info("短信发送成功: 手机号={}, BizID={}, 请求ID={}",
+                         phone_numbers, resp.body.biz_id, resp.body.request_id)
+                return True
+
+            log.warning("短信发送异常: 手机号={}, 响应={}", phone_numbers, resp_dict)
+            raise CustomException(f"{resp.body.message}")
+
         except Exception as error:
-            # 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
-            # 错误 message
-            print(error.message)
-            # 诊断地址
-            print(error.data.get("Recommend"))
+            # 打印异常栈
+            error_message = str(error.message) if hasattr(error, 'message') else str(error)
+            raise CustomException(f"短信发送失败: {error_message}")

+ 3 - 3
backend/tests/test_apikey_sign.py

@@ -5,7 +5,7 @@ class TestApiKeySign(unittest.TestCase):
 
     def test_qq(self):
         data = {
-            "third_biz_no": "123424202604270001"
+            "third_biz_no": "123424202604270077"
         }
         sign = TenantApiKeyService.generate_signature(
             "c0764f3e3a0af4bec292ceaf8b38d054d64e41bff36333a7f24d7cafada7cba98b8ac4dcac49367907e51b0b4e1566dec473198ad729ad1cbd5734b70322d7cd",
@@ -16,8 +16,8 @@ class TestApiKeySign(unittest.TestCase):
         data = {
             "account_book_id": "2088480770941200",
             "amount": 1.00,
-            "order_title": "Apikey转账6",
-            "third_biz_no": "123424202604270066",
+            "order_title": "Apikey转账7",
+            "third_biz_no": "123424202604270077",
             "payee_info": {
                 "identity_type": "ALIPAY_ACCOUNT",
                 "name": "钱红武",

+ 2 - 2
backend/tests/test_sms.py

@@ -8,8 +8,8 @@ class SmsTestCase(unittest.IsolatedAsyncioTestCase):
     def test_sms(self):
         request = SendSmsRequest(
             phone_numbers="13874410370",
-            template_code=SmsTemplateEnum.VERIFICATION_CODE.value.template_code,
-            template_param=SmsTemplateEnum.VERIFICATION_CODE.value.template_param_fn(code="111111"),
+            template_code=SmsTemplateEnum.VERIFICATION_CODE.value.template_name,
+            template_param=SmsTemplateEnum.VERIFICATION_CODE.value.template_param_fn(code="222111"),
         )
         asyncio.get_event_loop().run_until_complete(SmsSender.send_sms(request))
 

+ 3 - 1
frontend/src/api/module_system/auth.ts

@@ -136,10 +136,12 @@ export interface AutoLoginToken {
 /** 获取短信验证码请求 */
 export interface SmsCodeBody {
   mobile: string;
+  template_name: string;
 }
 
 /** 短信登录请求 */
 export interface SmsLoginBody {
-  phone: string;
+  mobile: string;
   code: string;
+  template_name: string;
 }

+ 17 - 1
frontend/src/store/modules/user.store.ts

@@ -1,6 +1,6 @@
 import { store, useTagsViewStore, usePermissionStoreHook, useDictStoreHook } from "@/store";
 
-import AuthAPI, { type LoginFormData } from "@/api/module_system/auth";
+import AuthAPI, { SmsLoginBody, type LoginFormData } from "@/api/module_system/auth";
 import UserAPI, { type UserInfo } from "@/api/module_system/user";
 import { type TenantInfo } from "@/api/module_system/tenant";
 import type { MenuTable } from "@/api/module_system/menu";
@@ -123,6 +123,22 @@ export const useUserStore = defineStore("user", {
       );
     },
 
+    async sms_login(smsForm: SmsLoginBody) {
+      const response = await AuthAPI.smsLogin(smsForm);
+      if (response.data.code === ResultEnum.SUCCESS) {
+        ElNotification({
+          title: "通知",
+          message: response.data.msg,
+          type: "success",
+        });
+      }
+      Auth.setTokens(
+        response.data.data.access_token,
+        response.data.data.refresh_token,
+        this.rememberMe
+      );
+    },
+
     // 登出
     async logout() {
       const response = await AuthAPI.logout({ token: Auth.getAccessToken() });

+ 24 - 31
frontend/src/views/module_system/auth/components/Login.vue

@@ -53,12 +53,12 @@
             </div>
           </el-form-item>
 
-          <div class="flex-x-between w-full">
+          <!-- <div class="flex-x-between w-full">
             <el-checkbox v-model="loginForm.remember">{{ t("login.rememberMe") }}</el-checkbox>
-            <!-- <el-link type="primary" underline="never" @click="toOtherForm('resetPwd')">
+            <el-link type="primary" underline="never" @click="toOtherForm('resetPwd')">
               {{ t("login.forgetPassword") }}
-            </el-link> -->
-          </div>
+            </el-link>
+          </div> -->
 
           <!-- 登录按钮 -->
           <el-form-item>
@@ -70,12 +70,14 @@
       </el-tab-pane>
 
       <!-- 短信登录 -->
-      <!-- <el-tab-pane label="短信登录" name="sms">
+      <el-tab-pane label="短信登录" name="sms">
         <el-form ref="smsFormRef" :model="smsForm" :rules="smsRules" size="large" :validate-on-rule-change="false">
-          <el-form-item prop="phone">
-            <el-input v-model.trim="smsForm.phone" placeholder="请输入手机号" clearable>
+          <el-form-item prop="mobile">
+            <el-input v-model.trim="smsForm.mobile" placeholder="请输入手机号" clearable>
               <template #prefix>
-                <el-icon><Iphone /></el-icon>
+                <el-icon>
+                  <Iphone />
+                </el-icon>
               </template>
             </el-input>
           </el-form-item>
@@ -90,9 +92,9 @@
                   </el-icon>
                 </template>
               </el-input>
-              <el-button :disabled="smsCountdown > 0 || !smsForm.phone" :loading="smsCodeLoading" class="w-32"
+              <el-button :disabled="smsCountdown > 0 || !smsForm.mobile" :loading="smsCodeLoading" class="w-32"
                 @click="getSmsCode">
-                {{ smsCountdown > 0 ? `${smsCountdown}s` : '获取验证码' }}
+                {{ smsCountdown > 0 ? `${smsCountdown}s` : "获取验证码" }}
               </el-button>
             </div>
           </el-form-item>
@@ -103,7 +105,7 @@
             </el-button>
           </el-form-item>
         </el-form>
-      </el-tab-pane> -->
+      </el-tab-pane>
 
       <!-- 快速登录 -->
       <!-- <el-tab-pane v-if="autoLoginUsers.length > 0" label="快速登录" name="quick">
@@ -148,12 +150,12 @@
       </el-tab-pane> -->
     </el-tabs>
 
-    <!-- <div flex-center gap-10px>
+    <div flex-center gap-10px>
       <el-text size="default">{{ t("login.noAccount") }}</el-text>
       <el-link type="primary" underline="never" @click="toOtherForm('register')">
         {{ t("login.reg") }}
       </el-link>
-    </div> -->
+    </div>
 
     <!-- 第三方登录 -->
     <!-- <div class="third-party-login">
@@ -214,12 +216,13 @@ const smsCodeLoading = ref(false);
 const smsCountdown = ref(0);
 
 const smsForm = reactive({
-  phone: "",
+  mobile: "",
   code: "",
+  template_name: "verify",
 });
 
 const smsRules = computed(() => ({
-  phone: [
+  mobile: [
     { required: true, message: "请输入手机号", trigger: "blur" },
     { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号", trigger: "blur" },
   ],
@@ -478,13 +481,13 @@ function checkCapsLock(event: KeyboardEvent) {
 // 获取短信验证码
 async function getSmsCode() {
   try {
-    const valid = await smsFormRef.value?.validateField("phone");
+    const valid = await smsFormRef.value?.validateField("mobile");
     if (!valid) return;
 
     smsCodeLoading.value = true;
 
     // 调用后端API获取短信验证码
-    await AuthAPI.getSmsCode({ mobile: smsForm.phone });
+    await AuthAPI.getSmsCode({ mobile: smsForm.mobile, template_name: "verify" });
 
     // 开始倒计时
     smsCountdown.value = 60;
@@ -495,9 +498,9 @@ async function getSmsCode() {
       }
     }, 1000);
 
-    ElMessage.success("验证码已发送");
+    // ElMessage.success("验证码已发送");
   } catch (error: any) {
-    ElMessage.error(error?.response?.data?.msg || "获取验证码失败");
+    // ElMessage.error(error?.response?.data?.msg || "获取验证码失败");
   } finally {
     smsCodeLoading.value = false;
   }
@@ -511,23 +514,13 @@ async function handleSmsLogin() {
 
     smsLoading.value = true;
 
-    // 调用后端短信登录API
-    const response = await AuthAPI.smsLogin(smsForm);
-    const loginData = response.data.data;
-
-    // 设置登录状态
-    Auth.setTokens(loginData.access_token, loginData.refresh_token, true);
-
-    // 获取用户信息
-    await userStore.getUserInfo();
+    await userStore.sms_login(smsForm);
 
     // 跳转
     const redirect = resolveRedirectTarget(route.query);
     await router.replace(redirect);
 
-    ElMessage.success("登录成功");
-  } catch (error: any) {
-    ElMessage.error(error?.response?.data?.msg || "登录失败");
+    // @ts-ignore
   } finally {
     smsLoading.value = false;
   }

+ 5 - 35
frontend/src/views/module_system/auth/components/Register.vue

@@ -229,6 +229,7 @@ const submit = async () => {
 
     loading.value = true;
 
+    model.value.username = model.value.mobile;
     await UserAPI.registerUser(model.value);
     // 注册成功后,双向绑定回写父容器的用户名和密码,并切回登录
     presetUsername.value = model.value.username;
@@ -251,7 +252,7 @@ async function getSmsCode() {
     smsCodeLoading.value = true;
 
     // 调用后端API获取短信验证码
-    await AuthAPI.getSmsCode({ mobile: model.value.mobile });
+    await AuthAPI.getSmsCode({ mobile: model.value.mobile, template_name: "verify" });
 
     // 开始倒计时
     smsCountdown.value = 60;
@@ -262,44 +263,13 @@ async function getSmsCode() {
       }
     }, 1000);
 
-    ElMessage.success("验证码已发送");
+    // ElMessage.success("验证码已发送");
   } catch (error: any) {
-    ElMessage.error(error?.response?.data?.msg || "获取验证码失败");
+    // ElMessage.error(error?.response?.data?.msg || "获取验证码失败");
   } finally {
     smsCodeLoading.value = false;
   }
 }
 
-// 短信登录
-async function handleSmsLogin() {
-  try {
-    // const valid = await smsFormRef.value?.validate();
-    // if (!valid) return;
-
-    // smsLoading.value = true;
-
-    // // 调用后端短信登录API
-    // const response = await AuthAPI.smsLogin(smsForm);
-    // const loginData = response.data.data;
-
-    // // 设置登录状态
-    // Auth.setTokens(loginData.access_token, loginData.refresh_token, true);
-
-    // // 获取用户信息
-    // await userStore.getUserInfo();
-
-    // // 跳转
-    // const redirect = resolveRedirectTarget(route.query);
-    // await router.replace(redirect);
-
-    ElMessage.success("登录成功");
-  } catch (error: any) {
-    ElMessage.error(error?.response?.data?.msg || "登录失败");
-  } finally {
-    smsLoading.value = false;
-  }
-}
-
-
-onMounted(() => { });
+onMounted(() => {});
 </script>