forked from erp-dev/erp
feat: shipment change && version modelize
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.utils import timezone
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from api_v1.models import AppVersion
|
||||
|
||||
|
||||
APP_VERSION_CACHE_KEY = "app_version:latest"
|
||||
|
||||
@@ -25,6 +26,7 @@ def build_app_version_payload(
|
||||
download_url: str,
|
||||
force=False,
|
||||
publish_date=None,
|
||||
message: str = "",
|
||||
) -> dict:
|
||||
return {
|
||||
"latest_version": {
|
||||
@@ -35,24 +37,25 @@ def build_app_version_payload(
|
||||
"download_url": str(download_url or "").strip(),
|
||||
"force": bool(force),
|
||||
"publish_date": _coerce_publish_date(publish_date),
|
||||
"message": str(message or ""),
|
||||
}
|
||||
|
||||
|
||||
def get_app_version_payload() -> dict:
|
||||
cached_payload = cache.get(APP_VERSION_CACHE_KEY)
|
||||
if isinstance(cached_payload, dict):
|
||||
try:
|
||||
version = cached_payload["latest_version"]
|
||||
return build_app_version_payload(
|
||||
major=version["major"],
|
||||
minor=version["minor"],
|
||||
build=version["build"],
|
||||
download_url=cached_payload.get("download_url", ""),
|
||||
force=cached_payload["force"],
|
||||
publish_date=cached_payload["publish_date"],
|
||||
)
|
||||
except (KeyError, TypeError, ValueError):
|
||||
pass
|
||||
app_version = (
|
||||
AppVersion.objects.filter(is_current=True).order_by('-created_at').first()
|
||||
or AppVersion.objects.order_by('-created_at').first()
|
||||
)
|
||||
if app_version:
|
||||
return build_app_version_payload(
|
||||
major=app_version.major,
|
||||
minor=app_version.minor,
|
||||
build=app_version.build,
|
||||
download_url=app_version.download_url,
|
||||
force=app_version.force,
|
||||
publish_date=app_version.publish_date,
|
||||
message=app_version.message,
|
||||
)
|
||||
|
||||
return build_app_version_payload(
|
||||
major=getattr(settings, "APP_LATEST_VERSION_MAJOR", 0),
|
||||
@@ -61,6 +64,7 @@ def get_app_version_payload() -> dict:
|
||||
download_url=getattr(settings, "APP_DOWNLOAD_URL", ""),
|
||||
force=getattr(settings, "APP_FORCE_UPDATE", False),
|
||||
publish_date=getattr(settings, "APP_PUBLISH_DATE", None),
|
||||
message=getattr(settings, "APP_VERSION_MESSAGE", ""),
|
||||
)
|
||||
|
||||
|
||||
@@ -72,17 +76,27 @@ def set_cached_app_version_payload(
|
||||
download_url: str,
|
||||
force=False,
|
||||
publish_date=None,
|
||||
message: str = "",
|
||||
) -> dict:
|
||||
payload = build_app_version_payload(
|
||||
app_version = AppVersion.objects.create(
|
||||
major=major,
|
||||
minor=minor,
|
||||
build=build,
|
||||
download_url=download_url,
|
||||
force=force,
|
||||
publish_date=publish_date,
|
||||
publish_date=publish_date or timezone.localdate(),
|
||||
message=message,
|
||||
is_current=True,
|
||||
)
|
||||
return build_app_version_payload(
|
||||
major=app_version.major,
|
||||
minor=app_version.minor,
|
||||
build=app_version.build,
|
||||
download_url=app_version.download_url,
|
||||
force=app_version.force,
|
||||
publish_date=app_version.publish_date,
|
||||
message=app_version.message,
|
||||
)
|
||||
cache.set(APP_VERSION_CACHE_KEY, payload, timeout=None)
|
||||
return payload
|
||||
|
||||
|
||||
class AppVersionView(APIView):
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.test import TestCase, override_settings
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from api_v1.models import AppVersion
|
||||
from flower.app_version import APP_VERSION_CACHE_KEY
|
||||
|
||||
|
||||
@@ -40,9 +41,84 @@ class AppVersionAPITest(TestCase):
|
||||
"download_url": "https://example.com/app.apk",
|
||||
"force": True,
|
||||
"publish_date": "2026-07-08",
|
||||
"message": "",
|
||||
},
|
||||
)
|
||||
|
||||
def test_app_version_api_returns_current_model_version(self):
|
||||
AppVersion.objects.create(
|
||||
major=1,
|
||||
minor=0,
|
||||
build=100,
|
||||
download_url="https://example.com/old.apk",
|
||||
publish_date="2026-07-01",
|
||||
message="旧版本",
|
||||
)
|
||||
AppVersion.objects.create(
|
||||
major=2,
|
||||
minor=0,
|
||||
build=200,
|
||||
download_url="https://example.com/current.apk",
|
||||
force=True,
|
||||
publish_date="2026-07-08",
|
||||
message="请更新到最新版本",
|
||||
is_current=True,
|
||||
)
|
||||
|
||||
response = self.client.get("/api/app-version/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["latest_version"], {"major": 2, "minor": 0, "build": 200})
|
||||
self.assertEqual(response.data["download_url"], "https://example.com/current.apk")
|
||||
self.assertEqual(response.data["force"], True)
|
||||
self.assertEqual(response.data["publish_date"], "2026-07-08")
|
||||
self.assertEqual(response.data["message"], "请更新到最新版本")
|
||||
|
||||
def test_app_version_api_returns_newest_model_version_when_no_current(self):
|
||||
AppVersion.objects.create(
|
||||
major=1,
|
||||
minor=0,
|
||||
build=100,
|
||||
download_url="https://example.com/old.apk",
|
||||
publish_date="2026-07-01",
|
||||
)
|
||||
AppVersion.objects.create(
|
||||
major=1,
|
||||
minor=1,
|
||||
build=110,
|
||||
download_url="https://example.com/new.apk",
|
||||
publish_date="2026-07-02",
|
||||
message="最新创建版本",
|
||||
)
|
||||
|
||||
response = self.client.get("/api/app-version/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["latest_version"], {"major": 1, "minor": 1, "build": 110})
|
||||
self.assertEqual(response.data["download_url"], "https://example.com/new.apk")
|
||||
self.assertEqual(response.data["message"], "最新创建版本")
|
||||
|
||||
def test_app_version_current_flag_is_mutually_exclusive(self):
|
||||
first = AppVersion.objects.create(
|
||||
major=1,
|
||||
minor=0,
|
||||
build=100,
|
||||
download_url="https://example.com/first.apk",
|
||||
is_current=True,
|
||||
)
|
||||
second = AppVersion.objects.create(
|
||||
major=2,
|
||||
minor=0,
|
||||
build=200,
|
||||
download_url="https://example.com/second.apk",
|
||||
is_current=True,
|
||||
)
|
||||
|
||||
first.refresh_from_db()
|
||||
second.refresh_from_db()
|
||||
self.assertFalse(first.is_current)
|
||||
self.assertTrue(second.is_current)
|
||||
|
||||
def test_set_app_version_command_updates_cached_api_payload_with_defaults(self):
|
||||
output = StringIO()
|
||||
call_command(
|
||||
@@ -65,6 +141,7 @@ class AppVersionAPITest(TestCase):
|
||||
self.assertEqual(response.data["download_url"], "https://example.com/latest.apk")
|
||||
self.assertEqual(response.data["force"], False)
|
||||
self.assertEqual(response.data["publish_date"], timezone.localdate().isoformat())
|
||||
self.assertEqual(response.data["message"], "")
|
||||
self.assertIn("'build': 1001", output.getvalue())
|
||||
|
||||
def test_set_app_version_command_accepts_force_and_publish_date(self):
|
||||
@@ -82,6 +159,8 @@ class AppVersionAPITest(TestCase):
|
||||
"--force",
|
||||
"--publish-date",
|
||||
"2026-07-01",
|
||||
"--message",
|
||||
"强制升级说明",
|
||||
stdout=output,
|
||||
)
|
||||
|
||||
@@ -92,3 +171,4 @@ class AppVersionAPITest(TestCase):
|
||||
self.assertEqual(response.data["download_url"], "https://example.com/force.apk")
|
||||
self.assertEqual(response.data["force"], True)
|
||||
self.assertEqual(response.data["publish_date"], "2026-07-01")
|
||||
self.assertEqual(response.data["message"], "强制升级说明")
|
||||
|
||||
@@ -27,7 +27,7 @@ from rest_framework_simplejwt.settings import api_settings
|
||||
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
# TokenRefreshView,
|
||||
TokenRefreshView,
|
||||
)
|
||||
|
||||
from basic_info.models import EmployeeStatusEnum
|
||||
@@ -143,7 +143,7 @@ class CustomTokenObtainPairView(TokenObtainPairView):
|
||||
urlpatterns = [
|
||||
# JWT 登录
|
||||
path('api/auth/login/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||
# path('api/auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path('api/auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path('api/error-codes/', ErrorCodeListView.as_view(), name='error_code_list'),
|
||||
path('api/app-version/', AppVersionView.as_view(), name='app_version'),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user