feat(openapi): harden contract export and align ljwx-phone to spec
This commit is contained in:
@@ -9,6 +9,7 @@ name: OpenAPI 契约维护
|
||||
- 'ljwx-boot/**/*.java'
|
||||
- 'ljwx-boot/**/pom.xml'
|
||||
- 'ljwx-boot/api/openapi.json'
|
||||
- 'ljwx-boot/api/openapi.yaml'
|
||||
- '.gitea/workflows/openapi-contract.yml'
|
||||
|
||||
# Push 到主分支时更新基线
|
||||
@@ -17,6 +18,7 @@ name: OpenAPI 契约维护
|
||||
paths:
|
||||
- 'ljwx-boot/**/*.java'
|
||||
- 'ljwx-boot/**/pom.xml'
|
||||
- 'ljwx-boot/scripts/export-openapi.sh'
|
||||
|
||||
# 允许手动触发
|
||||
workflow_dispatch:
|
||||
@@ -85,12 +87,19 @@ jobs:
|
||||
./scripts/export-openapi.sh
|
||||
echo "OpenAPI 契约导出成功"
|
||||
|
||||
- name: 校验 ljwx-phone OpenAPI 同步
|
||||
run: |
|
||||
chmod +x scripts/validate-phone-openapi-sync.sh
|
||||
./scripts/validate-phone-openapi-sync.sh
|
||||
|
||||
# 7. 上传契约文件(用于调试)
|
||||
- name: 上传 OpenAPI 契约
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openapi-contract
|
||||
path: ljwx-boot/api/openapi.json
|
||||
path: |
|
||||
ljwx-boot/api/openapi.json
|
||||
ljwx-boot/api/openapi.yaml
|
||||
retention-days: 30
|
||||
|
||||
# 8. 检测破坏性变更(仅 PR)
|
||||
@@ -237,11 +246,18 @@ jobs:
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
if git diff --quiet api/openapi.json; then
|
||||
if git diff --quiet api/openapi.yaml; then
|
||||
echo "契约无变化,跳过提交"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
git add api/openapi.json api/openapi.yaml
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "契约无变化,跳过提交"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add api/openapi.json
|
||||
git commit -m "chore: update OpenAPI contract baseline [skip ci]"
|
||||
git push origin main
|
||||
|
||||
|
||||
@@ -10,10 +10,16 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
PORT="${PORT:-9998}"
|
||||
OUT="${OUT:-${PROJECT_ROOT}/api/openapi.json}"
|
||||
HEALTH_CHECK_URL="http://127.0.0.1:${PORT}/actuator/health"
|
||||
API_DOCS_URL="http://127.0.0.1:${PORT}/v3/api-docs"
|
||||
MAX_WAIT_TIME=120 # 最长等待时间(秒)
|
||||
CHECK_INTERVAL=2 # 健康检查间隔(秒)
|
||||
MONOREPO_ROOT="$(cd "${PROJECT_ROOT}/.." && pwd)"
|
||||
OUT_YAML="${OUT_YAML:-${PROJECT_ROOT}/api/openapi.yaml}"
|
||||
SYNC_ROOT="${SYNC_ROOT:-true}"
|
||||
FORCE_REMOTE="${FORCE_REMOTE:-false}"
|
||||
CURL_INSECURE="${CURL_INSECURE:-false}"
|
||||
HEALTH_CHECK_URL="${HEALTH_CHECK_URL:-http://127.0.0.1:${PORT}/actuator/health}"
|
||||
API_DOCS_URL="${API_DOCS_URL:-http://127.0.0.1:${PORT}/v3/api-docs}"
|
||||
API_DOCS_YAML_URL="${API_DOCS_YAML_URL:-http://127.0.0.1:${PORT}/v3/api-docs.yaml}"
|
||||
MAX_WAIT_TIME="${MAX_WAIT_TIME:-120}" # 最长等待时间(秒)
|
||||
CHECK_INTERVAL="${CHECK_INTERVAL:-2}" # 健康检查间隔(秒)
|
||||
|
||||
# 颜色定义
|
||||
GREEN='\033[0;32m'
|
||||
@@ -27,6 +33,16 @@ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
|
||||
curl_fetch() {
|
||||
local url="$1"
|
||||
local out="$2"
|
||||
if [ "${CURL_INSECURE}" = "true" ]; then
|
||||
curl -k -fsS "${url}" -o "${out}"
|
||||
else
|
||||
curl -fsS "${url}" -o "${out}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==================== 环境检查 ====================
|
||||
check_dependencies() {
|
||||
local missing_deps=()
|
||||
@@ -44,9 +60,45 @@ check_dependencies() {
|
||||
fi
|
||||
}
|
||||
|
||||
validate_required_paths() {
|
||||
local spec_file="$1"
|
||||
local required_paths=(
|
||||
"/auth/user_name"
|
||||
"/mobile/user/info"
|
||||
"/mobile/health/data"
|
||||
"/mobile/messages"
|
||||
"/mobile/alerts"
|
||||
"/mobile/alerts/process"
|
||||
"/mobile/reset_password"
|
||||
"/upload_health_data"
|
||||
"/upload_device_info"
|
||||
"/upload_common_event"
|
||||
)
|
||||
|
||||
local missing_count=0
|
||||
for path in "${required_paths[@]}"; do
|
||||
if ! jq -e --arg p "${path}" --arg p2 "/api${path}" '.paths[$p] != null or .paths[$p2] != null' "${spec_file}" >/dev/null 2>&1; then
|
||||
log_error "契约缺少关键路径: ${path}"
|
||||
missing_count=$((missing_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${missing_count}" -gt 0 ]; then
|
||||
log_error "OpenAPI 不完整,缺少 ${missing_count} 个关键路径"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "关键路径完整性检查通过"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ==================== 应用状态检查 ====================
|
||||
is_app_running() {
|
||||
curl -fsS "${HEALTH_CHECK_URL}" >/dev/null 2>&1
|
||||
if [ "${CURL_INSECURE}" = "true" ]; then
|
||||
curl -k -fsS "${HEALTH_CHECK_URL}" >/dev/null 2>&1
|
||||
else
|
||||
curl -fsS "${HEALTH_CHECK_URL}" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_app_ready() {
|
||||
@@ -120,13 +172,14 @@ cleanup_on_error() {
|
||||
|
||||
# ==================== OpenAPI 导出 ====================
|
||||
export_openapi() {
|
||||
log_info "导出 OpenAPI 契约..."
|
||||
log_info "导出 OpenAPI 契约(JSON + YAML)..."
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$(dirname "${OUT}")"
|
||||
mkdir -p "$(dirname "${OUT_YAML}")"
|
||||
|
||||
# 获取 OpenAPI JSON
|
||||
if ! curl -fsS "${API_DOCS_URL}" -o "${OUT}.tmp"; then
|
||||
if ! curl_fetch "${API_DOCS_URL}" "${OUT}.tmp"; then
|
||||
log_error "无法访问 ${API_DOCS_URL}"
|
||||
return 1
|
||||
fi
|
||||
@@ -143,6 +196,33 @@ export_openapi() {
|
||||
jq '.' "${OUT}.tmp" > "${OUT}"
|
||||
rm -f "${OUT}.tmp"
|
||||
|
||||
# 关键路径完整性校验
|
||||
if ! validate_required_paths "${OUT}"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 获取 OpenAPI YAML
|
||||
if ! curl_fetch "${API_DOCS_YAML_URL}" "${OUT_YAML}.tmp"; then
|
||||
log_error "无法访问 ${API_DOCS_YAML_URL}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! grep -q "^openapi:" "${OUT_YAML}.tmp"; then
|
||||
log_error "返回的 YAML 缺少 openapi 头"
|
||||
rm -f "${OUT_YAML}.tmp"
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "${OUT_YAML}.tmp" "${OUT_YAML}"
|
||||
|
||||
# 同步 monorepo 根目录契约,避免多份漂移
|
||||
if [ "${SYNC_ROOT}" = "true" ]; then
|
||||
mkdir -p "${MONOREPO_ROOT}/api" "${MONOREPO_ROOT}/openapi"
|
||||
cp "${OUT}" "${MONOREPO_ROOT}/api/openapi.json"
|
||||
cp "${OUT_YAML}" "${MONOREPO_ROOT}/openapi/openapi.yaml"
|
||||
log_success "已同步根目录契约: api/openapi.json, openapi/openapi.yaml"
|
||||
fi
|
||||
|
||||
# 提取关键信息
|
||||
local api_version=$(jq -r '.info.version // "unknown"' "${OUT}")
|
||||
local api_title=$(jq -r '.info.title // "unknown"' "${OUT}")
|
||||
@@ -155,7 +235,8 @@ export_openapi() {
|
||||
log_info " 版本: ${api_version}"
|
||||
log_info " 接口数: ${paths_count}"
|
||||
log_info " Schema数: ${schemas_count}"
|
||||
log_info " 文件大小: $(ls -lh "${OUT}" | awk '{print $5}')"
|
||||
log_info " JSON大小: $(ls -lh "${OUT}" | awk '{print $5}')"
|
||||
log_info " YAML大小: $(ls -lh "${OUT_YAML}" | awk '{print $5}')"
|
||||
}
|
||||
|
||||
# ==================== 主流程 ====================
|
||||
@@ -170,7 +251,10 @@ main() {
|
||||
check_dependencies
|
||||
|
||||
# 2. 检查应用是否已运行
|
||||
if is_app_running; then
|
||||
if [ "${FORCE_REMOTE}" = "true" ]; then
|
||||
log_info "FORCE_REMOTE=true,跳过本地启动,直接导出..."
|
||||
app_was_running=true
|
||||
elif is_app_running; then
|
||||
log_info "检测到应用已在运行,直接导出..."
|
||||
app_was_running=true
|
||||
else
|
||||
|
||||
+22
-1
@@ -229,6 +229,27 @@ lib/
|
||||
└── 📊 utils/ # 工具类
|
||||
```
|
||||
|
||||
## OpenAPI 同步
|
||||
|
||||
`ljwx-phone` 现已基于后端 OpenAPI 契约统一接口路径常量,避免手写 URL 漂移。
|
||||
|
||||
1. 导出后端最新契约
|
||||
```bash
|
||||
cd ljwx-boot
|
||||
./scripts/export-openapi.sh
|
||||
```
|
||||
|
||||
2. 生成移动端接口常量
|
||||
```bash
|
||||
cd ../ljwx-phone
|
||||
./tool/generate_openapi_endpoints.sh
|
||||
```
|
||||
|
||||
3. 校验同步(仓库根目录)
|
||||
```bash
|
||||
./scripts/validate-phone-openapi-sync.sh
|
||||
```
|
||||
|
||||
## 🔧 安装与运行
|
||||
|
||||
### 前置要求
|
||||
@@ -387,4 +408,4 @@ double calculateHealthScore() {
|
||||
**Built with ❤️ by ljwx Team**
|
||||
|
||||
`Last Updated: 2025-08-29`
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../models/risk_alert.dart';
|
||||
import '../models/ai_health_suggestion.dart';
|
||||
import 'auth_service_v2.dart';
|
||||
import 'cache_service.dart';
|
||||
import 'openapi_endpoints.dart';
|
||||
import '../utils/error_handler.dart';
|
||||
import '../utils/logger.dart';
|
||||
|
||||
@@ -211,7 +212,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _dio.get('/mobile/user/info');
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileUserInfo);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
final data = response.data['data'];
|
||||
@@ -241,7 +242,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _dio.get('/mobile/user/info');
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileUserInfo);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
final data = response.data['data'];
|
||||
@@ -286,7 +287,7 @@ class ApiClient {
|
||||
// User context (userId, customerId, orgId) is automatically injected by backend
|
||||
// No need to pass these parameters manually
|
||||
|
||||
final response = await _dio.get('/mobile/health/data', queryParameters: queryParams);
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileHealthData, queryParameters: queryParams);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
final records = response.data['data']['records'] as List;
|
||||
@@ -373,7 +374,7 @@ class ApiClient {
|
||||
}) async {
|
||||
try {
|
||||
// Use new mobile API endpoint
|
||||
final response = await _dio.get('/mobile/messages', queryParameters: {
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileMessages, queryParameters: {
|
||||
'page': page,
|
||||
'pageSize': pageSize,
|
||||
if (status != null) 'status': status,
|
||||
@@ -426,7 +427,7 @@ class ApiClient {
|
||||
String? level,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.get('/mobile/alerts', queryParameters: {
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileAlerts, queryParameters: {
|
||||
'page': page,
|
||||
'pageSize': pageSize,
|
||||
if (status != null) 'status': status,
|
||||
@@ -455,7 +456,7 @@ class ApiClient {
|
||||
/// 获取未处理告警数量
|
||||
Future<int> getUnprocessedAlertCount() async {
|
||||
try {
|
||||
final response = await _dio.get('/mobile/alerts/unprocessed/count');
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileAlertsUnprocessedCount);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
return (response.data['data'] as num?)?.toInt() ?? 0;
|
||||
@@ -481,7 +482,7 @@ class ApiClient {
|
||||
|
||||
return await _retryOnNetworkError<GlobalHealthStatus?>(
|
||||
() async {
|
||||
final response = await _dio.get('/mobile/health/status/global');
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileHealthStatusGlobal);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
final status = GlobalHealthStatus.fromJson(response.data['data']);
|
||||
@@ -510,7 +511,7 @@ class ApiClient {
|
||||
|
||||
final result = await _retryOnNetworkError<List<HealthMetric>>(
|
||||
() async {
|
||||
final response = await _dio.get('/mobile/health/metrics/key');
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileHealthMetricsKey);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
final List<dynamic> data = response.data['data'];
|
||||
@@ -542,7 +543,7 @@ class ApiClient {
|
||||
|
||||
return await _retryOnNetworkError<RiskAlert?>(
|
||||
() async {
|
||||
final response = await _dio.get('/mobile/health/alerts/summary', queryParameters: {
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileHealthAlertsSummary, queryParameters: {
|
||||
'hours': hours,
|
||||
});
|
||||
|
||||
@@ -576,7 +577,7 @@ class ApiClient {
|
||||
|
||||
return await _retryOnNetworkError<AIHealthSuggestion?>(
|
||||
() async {
|
||||
final response = await _dio.get('/mobile/health/ai/suggestions');
|
||||
final response = await _dio.get(OpenApiEndpoints.mobileHealthAiSuggestions);
|
||||
|
||||
if (response.data['code'] == 200) {
|
||||
final data = response.data['data'];
|
||||
@@ -597,7 +598,7 @@ class ApiClient {
|
||||
/// 处理告警
|
||||
Future<bool> processAlert(String alertId, String status) async {
|
||||
try {
|
||||
final response = await _dio.post('/t_alert_info/process', data: {
|
||||
final response = await _dio.post(OpenApiEndpoints.mobileAlertsProcess, data: {
|
||||
'alertId': alertId,
|
||||
'status': status,
|
||||
'processedTime': DateTime.now().toIso8601String(),
|
||||
@@ -613,7 +614,7 @@ class ApiClient {
|
||||
/// 上传健康数据(保留兼容旧接口)
|
||||
Future<bool> uploadHealthData(Map<String, dynamic> healthData) async {
|
||||
try {
|
||||
final response = await _dio.post('/upload_health_data', data: healthData);
|
||||
final response = await _dio.post(OpenApiEndpoints.uploadHealthData, data: healthData);
|
||||
|
||||
return response.statusCode == 200;
|
||||
} catch (e) {
|
||||
@@ -625,7 +626,7 @@ class ApiClient {
|
||||
/// 上传设备信息(保留兼容旧接口)
|
||||
Future<bool> uploadDeviceInfo(Map<String, dynamic> deviceInfo) async {
|
||||
try {
|
||||
final response = await _dio.post('/upload_device_info', data: deviceInfo);
|
||||
final response = await _dio.post(OpenApiEndpoints.uploadDeviceInfo, data: deviceInfo);
|
||||
|
||||
return response.statusCode == 200;
|
||||
} catch (e) {
|
||||
@@ -638,7 +639,7 @@ class ApiClient {
|
||||
Future<bool> resetPassword(String oldPassword, String newPassword) async {
|
||||
try {
|
||||
// Use mobile API endpoint
|
||||
final response = await _dio.post('/mobile/reset_password', data: {
|
||||
final response = await _dio.post(OpenApiEndpoints.mobileResetPassword, data: {
|
||||
'oldPassword': oldPassword,
|
||||
'newPassword': newPassword,
|
||||
});
|
||||
@@ -653,7 +654,7 @@ class ApiClient {
|
||||
/// 更新用户信息
|
||||
Future<bool> updateUserInfo(Map<String, dynamic> userInfo) async {
|
||||
try {
|
||||
final response = await _dio.put('/sys/user/info', data: userInfo);
|
||||
final response = await _dio.put(OpenApiEndpoints.authUserInfo, data: userInfo);
|
||||
|
||||
return response.data['code'] == 200;
|
||||
} catch (e) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// GENERATED FILE - DO NOT EDIT MANUALLY
|
||||
// Generated by: ljwx-phone/tool/generate_openapi_endpoints.sh
|
||||
// Source: ljwx-boot/api/openapi.json
|
||||
|
||||
class OpenApiEndpoints {
|
||||
OpenApiEndpoints._();
|
||||
|
||||
static const mobileUserInfo = '/mobile/user/info';
|
||||
static const mobileHealthData = '/mobile/health/data';
|
||||
static const mobileMessages = '/mobile/messages';
|
||||
static const mobileAlerts = '/mobile/alerts';
|
||||
static const mobileAlertsUnprocessedCount = '/mobile/alerts/unprocessed/count';
|
||||
static const mobileHealthStatusGlobal = '/mobile/health/status/global';
|
||||
static const mobileHealthMetricsKey = '/mobile/health/metrics/key';
|
||||
static const mobileHealthAlertsSummary = '/mobile/health/alerts/summary';
|
||||
static const mobileHealthAiSuggestions = '/mobile/health/ai/suggestions';
|
||||
static const mobileAlertsProcess = '/mobile/alerts/process';
|
||||
static const mobileResetPassword = '/mobile/reset_password';
|
||||
|
||||
static const authUserInfo = '/auth/user_info';
|
||||
|
||||
// Compatibility upload endpoints retained for device-side integration.
|
||||
static const uploadHealthData = '/upload_health_data';
|
||||
static const uploadDeviceInfo = '/upload_device_info';
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PHONE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
REPO_ROOT="$(cd "${PHONE_ROOT}/.." && pwd)"
|
||||
OPENAPI_JSON="${OPENAPI_JSON:-${REPO_ROOT}/ljwx-boot/api/openapi.json}"
|
||||
OUT_FILE="${OUT_FILE:-${PHONE_ROOT}/lib/services/openapi_endpoints.dart}"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[ERROR] jq is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${OPENAPI_JSON}" ]]; then
|
||||
echo "[ERROR] OpenAPI contract not found: ${OPENAPI_JSON}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
has_path() {
|
||||
local p="$1"
|
||||
jq -e --arg p "${p}" '.paths[$p] != null' "${OPENAPI_JSON}" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
resolve_path() {
|
||||
local canonical="$1"
|
||||
if has_path "${canonical}"; then
|
||||
echo "${canonical}"
|
||||
return 0
|
||||
fi
|
||||
if has_path "/api${canonical}"; then
|
||||
echo "/api${canonical}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
declare -A resolved
|
||||
declare -A required_map=(
|
||||
[mobileUserInfo]="/mobile/user/info"
|
||||
[mobileHealthData]="/mobile/health/data"
|
||||
[mobileMessages]="/mobile/messages"
|
||||
[mobileAlerts]="/mobile/alerts"
|
||||
[mobileAlertsUnprocessedCount]="/mobile/alerts/unprocessed/count"
|
||||
[mobileHealthStatusGlobal]="/mobile/health/status/global"
|
||||
[mobileHealthMetricsKey]="/mobile/health/metrics/key"
|
||||
[mobileHealthAlertsSummary]="/mobile/health/alerts/summary"
|
||||
[mobileHealthAiSuggestions]="/mobile/health/ai/suggestions"
|
||||
[mobileAlertsProcess]="/mobile/alerts/process"
|
||||
[mobileResetPassword]="/mobile/reset_password"
|
||||
[authUserInfo]="/auth/user_info"
|
||||
[uploadHealthData]="/upload_health_data"
|
||||
[uploadDeviceInfo]="/upload_device_info"
|
||||
)
|
||||
|
||||
missing=0
|
||||
for key in "${!required_map[@]}"; do
|
||||
canonical="${required_map[$key]}"
|
||||
if ! resolved_value="$(resolve_path "${canonical}")"; then
|
||||
echo "[ERROR] missing endpoint in OpenAPI: ${canonical} (or /api${canonical})"
|
||||
missing=$((missing + 1))
|
||||
continue
|
||||
fi
|
||||
resolved["$key"]="${resolved_value}"
|
||||
done
|
||||
|
||||
if [[ "${missing}" -gt 0 ]]; then
|
||||
echo "[ERROR] OpenAPI is incomplete for ljwx-phone, missing=${missing}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "${OUT_FILE}")"
|
||||
|
||||
cat > "${OUT_FILE}" <<EOF
|
||||
// GENERATED FILE - DO NOT EDIT MANUALLY
|
||||
// Generated by: ljwx-phone/tool/generate_openapi_endpoints.sh
|
||||
// Source: ljwx-boot/api/openapi.json
|
||||
|
||||
class OpenApiEndpoints {
|
||||
OpenApiEndpoints._();
|
||||
|
||||
static const mobileUserInfo = '${resolved[mobileUserInfo]}';
|
||||
static const mobileHealthData = '${resolved[mobileHealthData]}';
|
||||
static const mobileMessages = '${resolved[mobileMessages]}';
|
||||
static const mobileAlerts = '${resolved[mobileAlerts]}';
|
||||
static const mobileAlertsUnprocessedCount = '${resolved[mobileAlertsUnprocessedCount]}';
|
||||
static const mobileHealthStatusGlobal = '${resolved[mobileHealthStatusGlobal]}';
|
||||
static const mobileHealthMetricsKey = '${resolved[mobileHealthMetricsKey]}';
|
||||
static const mobileHealthAlertsSummary = '${resolved[mobileHealthAlertsSummary]}';
|
||||
static const mobileHealthAiSuggestions = '${resolved[mobileHealthAiSuggestions]}';
|
||||
static const mobileAlertsProcess = '${resolved[mobileAlertsProcess]}';
|
||||
static const mobileResetPassword = '${resolved[mobileResetPassword]}';
|
||||
|
||||
static const authUserInfo = '${resolved[authUserInfo]}';
|
||||
|
||||
// Compatibility upload endpoints retained for device-side integration.
|
||||
static const uploadHealthData = '${resolved[uploadHealthData]}';
|
||||
static const uploadDeviceInfo = '${resolved[uploadDeviceInfo]}';
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "[SUCCESS] generated: ${OUT_FILE}"
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
echo "[INFO] generating ljwx-phone OpenAPI endpoint constants..."
|
||||
chmod +x ljwx-phone/tool/generate_openapi_endpoints.sh
|
||||
ljwx-phone/tool/generate_openapi_endpoints.sh
|
||||
|
||||
if ! git diff --quiet -- ljwx-phone/lib/services/openapi_endpoints.dart; then
|
||||
echo "[ERROR] ljwx-phone OpenAPI endpoint constants are out of sync."
|
||||
echo "[HINT] run: ljwx-phone/tool/generate_openapi_endpoints.sh"
|
||||
echo "[HINT] then commit: ljwx-phone/lib/services/openapi_endpoints.dart"
|
||||
git --no-pager diff -- ljwx-phone/lib/services/openapi_endpoints.dart | sed -n '1,200p'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[SUCCESS] ljwx-phone OpenAPI endpoint constants are in sync."
|
||||
Reference in New Issue
Block a user