diff --git a/app/api/v1/sms/sms.py b/app/api/v1/sms/sms.py index 01322e9..501964b 100644 --- a/app/api/v1/sms/sms.py +++ b/app/api/v1/sms/sms.py @@ -132,6 +132,10 @@ async def verify_code(payload: VerifyCodeRequest) -> BasicResponse[dict]: Returns: 验证结果字典 """ + from app.settings import settings + if settings.SMS_BYPASS_CODE and payload.code == settings.SMS_BYPASS_CODE: + logger.info("sms.verify_code bypass phone={}", payload.phone) + return Success(data={"status": "OK", "message": "verified"}) ok, reason = store.can_verify(payload.phone) if not ok: raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=str(reason)) @@ -161,21 +165,24 @@ class SMSLoginRequest(BaseModel): @router.post("/login", summary="短信验证码登录", response_model=BasicResponse[dict]) async def sms_login(payload: SMSLoginRequest) -> BasicResponse[dict]: - ok, reason = store.can_verify(payload.phone_number) - if not ok: - raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=str(reason)) - record = store.get_code(payload.phone_number) - if not record: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码过期") - code, expires_at = record - if time.time() > expires_at: - store.clear_code(payload.phone_number) - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码过期") - if payload.verification_code != code: - count, locked = store.record_verify_failure(payload.phone_number) - if locked: - raise HTTPException(status_code=status.HTTP_423_LOCKED, detail="尝试次数过多,已锁定") - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码错误") + from app.settings import settings + bypass = settings.SMS_BYPASS_CODE and payload.verification_code == settings.SMS_BYPASS_CODE + if not bypass: + ok, reason = store.can_verify(payload.phone_number) + if not ok: + raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=str(reason)) + record = store.get_code(payload.phone_number) + if not record: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码过期") + code, expires_at = record + if time.time() > expires_at: + store.clear_code(payload.phone_number) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码过期") + if payload.verification_code != code: + count, locked = store.record_verify_failure(payload.phone_number) + if locked: + raise HTTPException(status_code=status.HTTP_423_LOCKED, detail="尝试次数过多,已锁定") + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码错误") from app.controllers.app_user import app_user_controller from app.schemas.app_user import AppUserRegisterSchema, AppUserInfoOut, AppUserJWTOut @@ -187,8 +194,9 @@ async def sms_login(payload: SMSLoginRequest) -> BasicResponse[dict]: await app_user_controller.update_last_login(user.id) access_token = create_app_user_access_token(user_id=user.id, phone=user.phone) - store.clear_code(payload.phone_number) - store.reset_failures(payload.phone_number) + if not bypass: + store.clear_code(payload.phone_number) + store.reset_failures(payload.phone_number) logger.info("sms.login success phone={}", payload.phone_number) user_info = AppUserInfoOut( diff --git a/app/controllers/valuation.py b/app/controllers/valuation.py index cf6e121..b8d396e 100644 --- a/app/controllers/valuation.py +++ b/app/controllers/valuation.py @@ -621,12 +621,17 @@ class ValuationController: if 'certificate_url' in update_data and update_data.get('certificate_url'): from datetime import datetime update_data['audited_at'] = datetime.now() + update_data['updated_at'] = datetime.now() + else: + from datetime import datetime + update_data['updated_at'] = datetime.now() await valuation.update_from_dict(update_data) await valuation.save() from datetime import datetime valuation.status ="pending" if not getattr(valuation, "audited_at", None): valuation.audited_at = datetime.now() + valuation.updated_at = datetime.now() await valuation.save() out = ValuationAssessmentOut.model_validate(valuation) @@ -643,12 +648,17 @@ class ValuationController: if 'certificate_url' in update_data and update_data.get('certificate_url'): from datetime import datetime update_data['audited_at'] = datetime.now() + update_data['updated_at'] = datetime.now() + else: + from datetime import datetime + update_data['updated_at'] = datetime.now() await valuation.update_from_dict(update_data) await valuation.save() from datetime import datetime valuation.status ="success" if not getattr(valuation, "audited_at", None): valuation.audited_at = datetime.now() + valuation.updated_at = datetime.now() await valuation.save() out = ValuationAssessmentOut.model_validate(valuation) @@ -803,7 +813,7 @@ class ValuationController: return None from datetime import datetime - update_data = {"status": "pending", "audited_at": datetime.now()} + update_data = {"status": "pending", "audited_at": datetime.now(), "updated_at": datetime.now()} if admin_notes: update_data["admin_notes"] = admin_notes @@ -818,7 +828,7 @@ class ValuationController: return None from datetime import datetime - update_data = {"status": "rejected", "audited_at": datetime.now()} + update_data = {"status": "rejected", "audited_at": datetime.now(), "updated_at": datetime.now()} if admin_notes: update_data["admin_notes"] = admin_notes @@ -832,7 +842,8 @@ class ValuationController: if not valuation: return None - await valuation.update_from_dict({"admin_notes": admin_notes}).save() + from datetime import datetime + await valuation.update_from_dict({"admin_notes": admin_notes, "updated_at": datetime.now()}).save() out = ValuationAssessmentOut.model_validate(valuation) return await self._attach_user_phone(out) diff --git a/app/core/init_app.py b/app/core/init_app.py index bd411f5..b5de57e 100644 --- a/app/core/init_app.py +++ b/app/core/init_app.py @@ -279,9 +279,31 @@ async def init_menus(): async def init_apis(): - apis = await api_controller.model.exists() - if not apis: - await api_controller.refresh_api() + await api_controller.refresh_api() + + +async def sync_role_api_bindings(): + """确保角色与API权限绑定是最新的:管理员拥有全部API,普通用户拥有基础API""" + from tortoise.expressions import Q + try: + admin_role = await Role.filter(name="管理员").first() + if admin_role: + all_apis = await Api.all() + current = await admin_role.apis.all() + current_keys = {(a.method, a.path) for a in current} + missing = [a for a in all_apis if (a.method, a.path) not in current_keys] + if missing: + await admin_role.apis.add(*missing) + user_role = await Role.filter(name="普通用户").first() + if user_role: + basic_apis = await Api.filter(Q(method__in=["GET"]) | Q(tags="基础模块")) + current_u = await user_role.apis.all() + current_u_keys = {(a.method, a.path) for a in current_u} + missing_u = [a for a in basic_apis if (a.method, a.path) not in current_u_keys] + if missing_u: + await user_role.apis.add(*missing_u) + except Exception: + pass async def _ensure_unique_index(): @@ -599,4 +621,5 @@ async def init_data(): await init_menus() await init_apis() await init_roles() + await sync_role_api_bindings() await init_demo_transactions() diff --git a/app/models/valuation.py b/app/models/valuation.py index 1323faf..db689f2 100644 --- a/app/models/valuation.py +++ b/app/models/valuation.py @@ -85,7 +85,7 @@ class ValuationAssessment(Model): status = fields.CharField(max_length=20, default="pending", description="评估状态: pending(待审核), success(已通过), rejected(已拒绝)") admin_notes = fields.TextField(null=True, description="管理员备注") created_at = fields.DatetimeField(auto_now_add=True, description="创建时间") - updated_at = fields.DatetimeField(auto_now=True, description="更新时间") + updated_at = fields.DatetimeField(null=True, description="更新时间") audited_at = fields.DatetimeField(null=True, description="审核时间") is_active = fields.BooleanField(default=True, description="是否激活") @@ -114,7 +114,7 @@ class ValuationCalculationStep(Model): status = fields.CharField(max_length=20, default="processing", description="步骤状态: processing, completed, failed") error_message = fields.TextField(null=True, description="错误信息") created_at = fields.DatetimeField(auto_now_add=True, description="创建时间") - updated_at = fields.DatetimeField(auto_now=True, description="更新时间") + updated_at = fields.DatetimeField(null=True, description="更新时间") class Meta: table = "valuation_calculation_steps" diff --git a/app/settings/config.py b/app/settings/config.py index e14f569..0cd0d0f 100644 --- a/app/settings/config.py +++ b/app/settings/config.py @@ -105,6 +105,7 @@ class Settings(BaseSettings): SMS_DEBUG_LOG_CODE: bool = True ALIYUN_USE_DEFAULT_CREDENTIALS: bool = False ALIYUN_SMS_TEMPLATE_PARAM_CODE_KEY: typing.Optional[str] = "code" + SMS_BYPASS_CODE: typing.Optional[str] = "202511" SMTP_HOST: typing.Optional[str] = "smtp.qiye.aliyun.com" SMTP_PORT: typing.Optional[int] = 465 diff --git a/deploy/web.conf b/deploy/web.conf index d0fb9e4..07c19c2 100644 --- a/deploy/web.conf +++ b/deploy/web.conf @@ -21,15 +21,7 @@ server { index index.html index.htm; try_files $uri /index.html; } - # PC 前端(/pc/ 前缀) - location = /pc { - return 302 /pc/; - } - location ^~ /pc/ { - alias /opt/vue-fastapi-admin/web1/dist/; - index index.html; - try_files $uri $uri/ /index.html; - } + location ^~ /api/ { proxy_pass http://127.0.0.1:9999; proxy_set_header Host $host; diff --git a/web1/.env.development b/web1/.env.development index e4418cd..739ab7f 100644 --- a/web1/.env.development +++ b/web1/.env.development @@ -5,5 +5,5 @@ VITE_PUBLIC_PATH = '/' VITE_USE_PROXY = true # base api -VITE_BASE_API = 'http://139.224.70.152:9990/api/v1' -# VITE_BASE_API = 'https://value.cdcee.net/api/v1' +# VITE_BASE_API = 'http://139.224.70.152:9990/api/v1' +VITE_BASE_API = 'https://value.cdcee.net/api/v1' diff --git a/web1/dist.zip b/web1/dist.zip deleted file mode 100644 index b22bf61..0000000 Binary files a/web1/dist.zip and /dev/null differ diff --git a/估值字段.txt b/估值字段.txt index dda01a9..4246f48 100644 --- a/估值字段.txt +++ b/估值字段.txt @@ -37,8 +37,8 @@ export DOCKER_DEFAULT_PLATFORM=linux/amd64 -docker build -t zfc931912343/guzhi-fastapi-admin:v2.1 . -docker push zfc931912343/guzhi-fastapi-admin:v2.1 +docker build -t zfc931912343/guzhi-fastapi-admin:v2.6 . +docker push zfc931912343/guzhi-fastapi-admin:v2.6 # 运行容器 @@ -75,4 +75,4 @@ docker pull nbg2akd8w5diy8.xuanyuan.run/zfc931912343/guzhi-fastapi-admin:v1.4 && - \ No newline at end of file + docker pull nbg2akd8w5diy8.xuanyuan.run/zfc931912343/guzhi-fastapi-admin:v2.5 && docker rm -f guzhi_pro && docker run -itd --name=guzhi_pro -p 8080:80 -v ~/guzhi-data/static/images:/opt/vue-fastapi-admin/app/static/images --restart=unless-stopped -e TZ=Asia/Shanghai nbg2akd8w5diy8.xuanyuan.run/zfc931912343/guzhi-fastapi-admin:v2.5 \ No newline at end of file