提交图

134 次代码提交

作者 SHA1 备注 提交日期
XuqmGroup
a98dbca26d fix(system-update): 用独立助手容器替代 CompletableFuture 实现 tenant-service 自重建
原方案:CompletableFuture 延迟调用 docker-compose up。
问题:docker-compose 发出 stop 指令后,容器内全部进程(含 CompletableFuture 线程)
     被立即杀死,rm/create/start 步骤永远不会执行,tenant-service 停在停止状态。

新方案:先用 docker run -d 启动独立助手容器(xuqm-self-updater),
     它不依附于 tenant-service,不会随之终止;8 秒后执行 force-recreate。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 14:52:36 +08:00
XuqmGroup
f2e126e2d0 feat(tenant-service): 一键更新接口 + Dockerfile 添加 docker-compose
- 新增 SystemUpdateController POST /api/system/update(PRIVATE 模式)
- SystemUpdateService 通过 docker-compose 拉镜像并逐服务重建容器
- Dockerfile 添加 docker-cli + docker-compose(用于容器内调用 Docker API)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 14:46:40 +08:00
XuqmGroup
8a3c41d5ff feat(license): 租户自主管理最大设备数,ops 彻底移除 license 管理
license-service:
- LicenseAdminController: 新增 PATCH /api/license/admin/apps/{appKey},
  租户可直接修改 maxDevices / isActive / remark

tenant-service:
- OpsController: 移除 GET /api/ops/apps/{appKey}/license 和
  PUT /api/ops/apps/{appKey}/license/max-devices 两个端点,
  同时移除 licenseServiceClient 字段注入
- LicenseServiceClient: 移除 updateMaxDevices() 和 getAppLicenseStatus()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 12:45:33 +08:00
XuqmGroup
af922ae420 fix(ci): 串行化生产部署防止并发 docker pull 竞争
多服务同时构建时 Deploy 阶段并发向同一台生产机 docker pull,
containerd content store 写入共享 layer 产生文件竞争导致 rename 失败。
加 lock('prod-deploy') 确保所有服务按序部署,并加 retry(3) 容忍偶发抖动。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 10:58:08 +08:00
XuqmGroup
25e9bef745 chore(version): 更新版本号到 2026.05.20-private.3
- 添加新版本号文件 VERSION
- 版本号设置为 2026.05.20-private.3
2026-05-21 10:44:59 +08:00
XuqmGroup
02ad5aad06 fix(private): 私有化部署 CORS 放开所有 Origin
私有化部署时客户使用自定义域名,原硬编码的 *.xuqinmin.com 白名单
导致 WebSocket 握手和跨域请求被 Spring Security CORS 过滤器拒绝(426/403)。
检测 DEPLOYMENT_MODE=PRIVATE 环境变量,私有化模式下允许所有 Origin。
影响范围:im-service / file-service / license-service / update-service。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 10:44:33 +08:00
XuqmGroup
897326ff0f feat(private): 新增内部维护接口自动处理积压 PENDING 申请
- SecurityConfig: 放开 /api/private/admin/** 无需 JWT
- FeatureServiceManager.autoApproveAllPending(): 批量审批所有 PENDING 记录
- OpsController: POST /api/private/admin/approve-pending-requests
  仅私有化模式可用,upgrade.sh 重启后自动调用,无需手动操作

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 18:45:07 +08:00
XuqmGroup
6ca0dcbe74 fix(private): 私有化模式下存量 PENDING 服务申请自动开通
之前的自动开通逻辑在重复申请检查之后,导致已有 PENDING 记录时
直接抛 400 而不进入自动开通流程。
现在私有化模式下检测到 PENDING 记录时直接 approveRequest,
不再返回"请等待运营人员处理"错误。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 18:32:32 +08:00
XuqmGroup
f9957143da feat(private): 私有化部署增强 — 服务自动开通、屏蔽 Ops 功能
- FeatureServiceManager: 私有化模式下服务开通申请跳过审核,直接自动激活
- OpsController: 私有化模式下 /api/auth/ops/login 返回 404,屏蔽运营登录
- OpsAdminInitializer: 私有化模式下跳过默认运营管理员账号的初始化

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 18:24:43 +08:00
XuqmGroup
4432c7dc28 fix(oppo): remap audit_status=5 to UNDER_REVIEW; restore REJECTED→UNDER_REVIEW in poll
OPPO returns audit_status=5 for versions currently under review (审核中);
previously this was mapped to REJECTED causing a state mismatch. Only 444
is now treated as the rejected aggregate code.

Also extend the scheduled poll to restore REJECTED→UNDER_REVIEW for any
store (not just Xiaomi) when the vendor API reports the version is under
review, covering cases where the submission succeeded after our platform
recorded an exception.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 10:59:52 +08:00
XuqmGroup
4d2faa33de fix(oppo): strip empty params from submit body to fix sign mismatch (errno=800004)
OPPO server includes all received body params in server-side signature computation;
sending empty-string optional params while excluding them from the client-side sign
produces a mismatch. Remove empty entries from params map before building both the
api_sign and the POST body so both sides operate on identical param sets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 10:16:41 +08:00
XuqmGroup
501d7e09ab fix(update): fix OPPO token expiry, sign empty params, and MI already-live detection
- OPPO: re-obtain access token after APK upload (prevents expiry during long uploads)
- OPPO: exclude empty-string params from api_sign computation per OPPO spec
- OPPO: include errno in error message for easier debugging
- MI: detect already-live version via packageInfo.versionCode before upload;
  throw VersionAlreadyLiveException → executor marks state APPROVED instead of REJECTED

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:11:13 +08:00
XuqmGroup
450a44de68 fix: isolate private deployment databases and fix store review refresh bugs
application.yml — all services:
- Replace hardcoded jdbc:mysql://39.107.53.187 with ${SPRING_DATASOURCE_URL:fallback}
- Same for SPRING_DATASOURCE_USERNAME/PASSWORD
- im-service: replace hardcoded redisdev.xuqinmin.com with ${SPRING_DATA_REDIS_*}
  This ensures docker-compose environment overrides take effect; without these
  placeholders, Spring Boot's relaxed binding couldn't override the YAML values
  and the private deployment connected to production databases.

StoreSubmissionService.refreshStoreReviewStatus — two bugs fixed:
1. MI/UNDER_REVIEW_XIAOMI branch now guards against downgrading APPROVED state.
   Xiaomi's poll API returns UNDER_REVIEW_XIAOMI when the submitted version is
   not yet the live version, even after the store approves it. Previously this
   caused the manual refresh to overwrite a webhook-confirmed APPROVED with
   UNDER_REVIEW on every click.
2. When the poll returns APPROVED but currentSubmissionLive=false (another version
   is live on the store), no longer overwrite an existing APPROVED (from webhook)
   with nonCurrentRelease=true. The webhook is authoritative; the live version
   difference just means distribution is pending, not that this is a non-current
   release. Only adds nonCurrentRelease when transitioning FROM a non-APPROVED
   state (true pre-existing detection).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:25:50 +08:00
XuqmGroup
9771663f00 fix(tenant): correct import endpoint path in SecurityConfig
/api/private/migrate/import → /api/private/deployment/migrate/import
to match PrivateDeploymentController's @RequestMapping("/api/private/deployment")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:07:39 +08:00
XuqmGroup
d007072ea9 fix(update): send webhook on store live detection (Xiaomi 已上架)
updateStoreReviewLive was calling IM notifier but not sendWebhook,
so polling-detected APPROVED events (e.g. Xiaomi going live) never
triggered the tenant's configured webhook URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:57:51 +08:00
XuqmGroup
f97201e3e3 feat(tenant): API-based tenant migration for private deployment
- Add MigrateController: request-code / generate-key / export endpoints
  with one-time pmk_ key (SHA-256 hashed, 24h expiry)
- Add PrivateDeploymentController import endpoint for private mode only
- Add MigrateKeyEntity / MigrateKeyRepository for key lifecycle
- Add MigrateExportData DTO (tenant + apps + feature services)
- Add AppEntity.isDefault / deletable fields
- Add AppRepository.deleteAllExcept / FeatureServiceRepository.deleteAllExcept
- Permit /api/migrate/export and /api/private/migrate/import in SecurityConfig

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:11:49 +08:00
XuqmGroup
e5f0e7faea feat: private deployment server-side capabilities (P2)
- PrivateDeploymentProperties: DEPLOYMENT_MODE/ENABLE_*/TENANT_BOOTSTRAP_ENABLED config binding
- PrivateTenantBootstrapInitializer: auto-create main tenant and app from env vars when PRIVATE mode, idempotent
- AuthService: block registration with XUQM_PRIVATE_2001 when TENANT_REGISTER_ENABLED=false
- EmailService: block REGISTER email verification in private mode
- SdkConfigController: intersect DB feature flags with ENABLE_* deployment flags for runtime degradation
- PrivateDeploymentController: GET /api/private/deployment/status public endpoint
- SecurityConfig: permit /api/private/deployment/status without auth
- application.yml: add deployment.* and tenant.bootstrap.* config sections with env var bindings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:49:46 +08:00
XuqmGroup
4d54d2a4a4 docs(private): 更新私有化SDK设计文档添加配置管理和错误码规范
- Flutter包名格式从xuqm_private_flutter_*改为xuqm_private_*
- 添加configVersion字段用于追踪配置变更,格式为YYYY.MM.DD-序号
- 添加配置版本管理说明,包含schemaVersion兼容性策略
- 添加配置热更新机制,支持冷加载和热加载两种方式
- 添加多环境配置支持,可通过activeProfile指定生效环境
- 添加统一错误码规范,覆盖配置缺失、格式错误、版本不兼容等场景
- 更新验收标准,包含热更新、多环境切换、错误码一致性要求
- 添加私有化开发计划中的错误码规范、文档同步、数据迁移等任务
- 补充MySQL运维细节、证书自动续期、监控告警、日志收集等部署要求
- 添加性能基准指标,包含HTTP API、WebSocket、文件传输等性能要求
- 修复应用商店审核状态轮询中的逻辑错误,添加小米商店特殊处理
- 更新前端界面显示审核版本信息,优化状态刷新逻辑
2026-05-18 19:17:44 +08:00
XuqmGroup
93fdb31cdc docs(private): 完善私有化部署开发计划和设计规范
- 增加实时进度和交接规则,定义任务状态枚举和更新格式
- 创建任务进度台账,涵盖P0-P5阶段全部开发任务
- 补充部署仓库交付边界确认和进度审计规范
- 完善MySQL/Redis双模式支持,增加external/managed选项
- 增加离线部署、安全治理、可观测性等完整交付能力
- 更新仓库结构设计,增加secrets.env、observability、data目录
- 补充健康检查、诊断脚本、升级回滚、备份恢复详细要求
- 优化应用商店审核状态查询逻辑,增加手动刷新接口
- 修复小米和VIVO商店状态查询中的版本匹配逻辑错误
- 增加缓存键版本隔离,防止不同版本状态混淆
- 优化厂商API连通性检查和审核状态轮询机制
2026-05-18 19:00:38 +08:00
XuqmGroup
87edb316a5 feat(private-deploy): 支持 MySQL/Redis 外部连接和托管模式部署
- 添加 external 和 managed 两种数据库/缓存模式支持
- 实现 MySQL/Redis 托管安装脚本和配置向导
- 支持客户自备连接或部署脚本新建基础设施
- 更新部署文档说明不同模式的配置和验证要求
- 添加应用版本防重复上传和删除功能
- 实现应用商店预提交检查和发布计划功能
2026-05-18 18:37:10 +08:00
XuqmGroup
e309a41ed0 docs(deploy): 移除 Jenkins 配置和 Android Demo 计划文档
- 删除 jenkins-setup.md 完整的 Jenkins 服务配置指南
- 更新 README.md 部署文档标题为公有化部署文档
- 添加私有化部署说明章节和相关设计文档链接
- 从 REST API 设计文档中移除 demo-service 相关描述
- 更新推送架构图中业务服务端描述为客户端服务器
- 删除 android-demo-plan.md Android Demo 开发计划文档
- 删除 multi-platform-im-roadmap.md 多平台 IM 路线图文档
- 删除 java-im-server-sdk-plan.md Java IM 服务端 SDK 计划文档
2026-05-18 17:57:05 +08:00
XuqmGroup
b3b33dbb7b fix: remove unreliable post-failure live-check; restrict REJECTED poll to HUAWEI
Post-failure live check called pollStoreSingleReviewState on submission
failure, but for MI/VIVO/OPPO/HONOR the poll returns APPROVED for ANY
live version (not version-specific). This caused false positives: if an
older version was live on Xiaomi, the poll would return APPROVED and
incorrectly mark the new submission as 已上线(直接上传).

Changes:
- Remove post-failure live check from executeSinglePlan and sequential
  failure handler — submission failures are just marked REJECTED
- Restrict the REJECTED-store poll to HUAWEI only, since it is the only
  store where pollStoreSingleReviewState is version-specific
  (compares onShelfVersionCode against v.getVersionCode())
- Log full VIVO data response to capture versionCode field name for
  future version-specific detection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:52:53 +08:00
XuqmGroup
8d46d21726 fix: store resubmission, Xiaomi curl, and live-on-store detection
- Dockerfile: add curl to Alpine runtime image (required by Xiaomi/Vivo
  APK upload which uses ProcessBuilder curl for HTTP/1.1 multipart)
- AppStoreService: mark previously-APPROVED stores as WITHDRAWN on
  resubmission so executeSubmitAsync calls the store cancel API first;
  guard updateStoreReview from stale APPROVED webhooks on PENDING/WITHDRAWN
  stores; add updateStoreReviewLive() to record pre-existing live versions
  with liveOnStore/preExisting metadata
- StoreSubmissionService: call cancelAtStore for WITHDRAWN stores before
  resubmitting; expand poll query to include REJECTED stores and call
  updateStoreReviewLive when a REJECTED store is found live; after any
  submission failure check if the store already has the version live
  (catches Huawei/Vivo pre-existing direct uploads at submission time)
- AppVersionRepository: add findAllWithUnderReviewOrRejectedStores query

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:30:26 +08:00
XuqmGroup
8de0338b93 fix: withdraw previously-approved store reviews before re-submitting same version
When re-submitting a version to stores where the previous review was APPROVED:

1. markSubmitted now sets those stores to WITHDRAWN (not PENDING), preserving
   the old batchId/submittedAt for traceability. This signals to executeSubmitAsync
   that the store cancel API must be called before a new submission is attempted.

2. executeSubmitAsync detects the WITHDRAWN state and calls cancelAtStore first,
   then falls through to the normal submission path. This revokes the old approval
   on the store's side so no stale webhook or poll cycle can fire APPROVED for the
   old review after re-submission.

3. updateStoreReview now rejects APPROVED transitions from PENDING or WITHDRAWN
   states (stale webhook guard). A valid approval can only arrive after the store
   has seen the new submission (i.e. current state must be SUBMITTING or UNDER_REVIEW).
   This prevents autoPublishAfterReview from triggering before the new review cycle.

Operation log includes `approvedWithdrawn` list when any store was withdrawn on re-submit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:09:31 +08:00
XuqmGroup
ab7f029960 feat(update-service): add PATCH /app/{id}/changelog with audit log
Allows editors to update release notes at any time. Every change is
recorded in update_operation_log with action CHANGELOG_UPDATE and
before/after values in detailJson, satisfying the audit requirement.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:47:36 +08:00
XuqmGroup
1ec7f2e35d fix(file-service): stream upload to disk to fix OOM on large files
file.getBytes() loaded the entire APK into JVM heap, causing
OutOfMemoryError on files >~50MB. Now streams to a temp file while
computing SHA-256 via DigestInputStream, then atomically moves to the
final path. Zero heap cost regardless of file size.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:31:15 +08:00
XuqmGroup
9c51e666f8 fix(file-service): permit /error endpoint to avoid 401 on server errors
Spring Boot's /error handler was secured, causing any server-side exception
during upload/serve to redirect clients to login instead of returning an
error message. Permitting /error ensures errors are returned as proper
JSON responses rather than auth challenges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:10:49 +08:00
XuqmGroup
8bc9b1ebda fix(file-service): use AntPathRequestMatcher to bypass Spring MVC matching
Spring Security 6 with MVC on classpath resolves requestMatchers(HttpMethod, String)
to MvcRequestMatcher, which fails to match the actual servlet paths for this service.
Switching to explicit AntPathRequestMatcher instances bypasses MVC introspection and
forces pure Ant pattern evaluation, fixing persistent 401 on public upload/serve endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:40:11 +08:00
XuqmGroup
b49b67bb1e fix(file-service): use explicit HttpMethod on all requestMatchers to force AntRequestMatcher
Spring Security 6 MvcRequestMatcher (used when no HttpMethod is specified
and Spring MVC is on the classpath) fails to match the upload endpoint,
falling through to anyRequest().authenticated() and returning 401.
Specifying HttpMethod forces AntRequestMatcher which matches reliably.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:11:13 +08:00
XuqmGroup
61b79465cd fix(file-service): restore public upload by explicitly allowing POST /api/file/upload
The previous commit (GET-only permitAll) inadvertently broke upload by
requiring auth. The original design intentionally allows unauthenticated
upload — explicitly permit POST /api/file/upload to make this clear.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:49:37 +08:00
XuqmGroup
623656648e fix(file-service): restrict file-serving permitAll to GET requests only
Upload endpoint (POST) was inadvertently matched by the method-less
requestMatchers("/api/file/*") rule. Making it GET-only makes the intent
explicit and ensures upload correctly requires a valid JWT.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:11:56 +08:00
XuqmGroup
4e54737e72 feat: webhook shows app name; auto-withdraw superseded approved stores
- Webhook notification body shows app display name (resolved from
  tenant-service via internal API with in-memory cache) instead of appKey
- When re-uploading a package with the same versionCode, automatically
  withdraw APPROVED store entries from the older entity before submitting
  the new entity, preventing duplicate active submissions
- tenant-service /internal/sdk/apps/{appKey}/platform-info now includes
  the app 'name' field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:31:31 +08:00
XuqmGroup
3bc8a39d0f fix: return 401 (not 403) for unauthenticated requests across all services
Spring Security's default Http403ForbiddenEntryPoint was returning 403
for all auth failures. Frontend clients treat 403 as a permission error
(not an auth error), so silent loops occurred instead of proper re-login.
Adding a custom AuthenticationEntryPoint that returns 401 makes clients
handle auth failures correctly (show login page on 401).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:31:24 +08:00
XuqmGroup
39367a57e1 fix MI poll: don't mistake old published version as new version approved
The MI /devupload/dev/query API returns the currently-published app's status,
not the pending submission. appStatus=3 means the previous version is still
online, not that the new submission was accepted. Use updateVersion=true as
the approval signal; default updateVersion to false to avoid false positives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 13:04:18 +08:00
XuqmGroup
81f04ee432 fix MI poll: log full response, fix synchroResult=0 mapped to REJECTED not UNDER_REVIEW
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:39:06 +08:00
XuqmGroup
f7dbce7268 fix HONOR poll endpoint and status mapping; improve all store polling reliability
- HONOR: use get-app-current-release endpoint (correct), auditResult field (0=review,1=approved,2=rejected)
- HONOR: assertHonorSuccess now accepts both "0" and "0000" success codes
- OPPO: add integer status mapping (111=approved, 444=rejected) from reference impl
- All stores: add full response body logging for diagnosing poll issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:15:19 +08:00
XuqmGroup
8dea96ef5e fix: skip already-reviewing stores on resubmit; clean up orphaned SUBMITTING states
- StoreSubmissionService: skip UNDER_REVIEW/APPROVED stores in preflight loop to prevent duplicate submissions
- StoreSubmissionService: post-batch sweep marks any still-SUBMITTING stores in same batch as REJECTED
- StoreSubmissionService: @EventListener(ApplicationReadyEvent) clears orphaned SUBMITTING states on startup
- AppVersionRepository: add findAllWithSubmittingStores() native query for startup sweep

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:58:27 +08:00
XuqmGroup
4136cd57b6 feat: add app transfer API, parallel store upload, DingTalk/WeCom/Feishu webhook formats
- OpsController/OpsService: POST /api/ops/apps/{appKey}/transfer to move app between tenants
- StoreSubmissionService: read parallelStoreUpload from publish config; conditional parallel vs sequential submission
- AppStoreService: support DINGTALK/WECOM/FEISHU/CUSTOM notify formats in sendWebhook()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:34:59 +08:00
XuqmGroup
57ad8f7f25 fix: app_key backfill on re-registration + notify after tx commit
- LicenseDeviceService: update app_key on re-registration if blank,
  fixing devices that registered before the app_key column was added
- FeatureServiceManager: send activation IM notification in afterCommit()
  hook so the frontend refresh sees the committed DB state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:04:57 +08:00
XuqmGroup
8443af4818 fix: add --http1.1 to MI curl upload to prevent HTTP/2 stream error (exit 92) 2026-05-16 13:28:50 +08:00
XuqmGroup
f977934f5a feat: remove Update methods from Java SDK, keep only IM and Push 2026-05-16 12:10:07 +08:00
Dev
316ecfcd38 perf: Maven BuildKit cache mount + pom-first layering + Alpine JRE; ci: Jenkinsfile固定main分支 2026-05-16 11:33:25 +08:00
XuqmGroup
e7dbdc2ef3 fix(update): use curl for Xiaomi APK upload to bypass server-side body timeout
RestTemplate drops large multipart payloads on the MI API server; switching to
ProcessBuilder curl with Expect:100-continue headers and a 130-minute timeout resolves
upload failures for large APKs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 02:24:04 +08:00
XuqmGroup
afb57a5d5f feat: ops应用列表支持租户筛选 + 返回租户名称 + 服务按类型展示
- AppRepository: 新增 JOIN 查询,返回应用+租户名称
- OpsService: listApps 支持 tenantId 筛选,getAppDetail 按 serviceType 去重
- OpsController: listApps 新增 tenantId 可选参数

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 00:24:10 +08:00
XuqmGroup
805ef3900b fix: Jenkins checkout 使用 BRANCH 参数而非 scm 默认分支
checkout scm 忽略 BRANCH 参数,使用 Jenkins 任务配置的 SCM 分支。
改为显式 checkout 并引用 params.BRANCH,确保构建最新代码。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:56:34 +08:00
XuqmGroup
10f0043f15 License服务改造:平台无关、ops管理最大设备数、有效期不可变
- LICENSE审批只创建1条FeatureServiceEntity记录(不分平台)
- FeatureServiceManager扩展平台无关查询到LICENSE
- LicenseServiceClient新增getAppLicenseStatus/updateMaxDevices方法
- OpsController新增license管理接口(GET状态、PUT最大设备数)
- AppLicenseService.update中expiresAt一旦设置不可修改
- 审批流程支持传入expiresAt参数

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 22:38:46 +08:00
XuqmGroup
0ed09a8229 一大波改动 2026-05-15 22:11:03 +08:00
XuqmGroup
bc1165d22e Align license service with app model 2026-05-15 21:42:10 +08:00
XuqmGroup
4f59fead0a Store license check user info 2026-05-15 21:29:48 +08:00
XuqmGroup
d5b8f03996 Fix license service activation visibility 2026-05-15 21:25:58 +08:00