feat: add PostgreSQL web installation wizard
Signed Release / release (push) Failing after 5m1s

This commit is contained in:
Qiufeng
2026-08-04 07:42:20 +08:00
parent b714f9851e
commit 1affcd9a5e
61 changed files with 1992 additions and 431 deletions
@@ -4,7 +4,6 @@ import com.kaidi.oa.domain.QualElecLicense;
import com.kaidi.oa.repository.QualElecLicenseRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.LocalDate;
@@ -17,7 +16,7 @@ import java.time.LocalDate;
*
* @Order(225) 在所有主数据 Seeder(最高 220)之后运行;幂等(count > 0 跳过)。
*/
@Component
@DemoSeed
@Order(225)
public class AdminQualElecSeeder implements CommandLineRunner {
@@ -14,7 +14,6 @@ import com.kaidi.oa.repository.StaffDossierItemRepository;
import com.kaidi.oa.repository.StaffDossierRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.Instant;
@@ -26,7 +25,7 @@ import java.time.LocalDate;
* 幂等 + 非破坏:以各表是否已有数据为哨兵,仅在空表时灌入少量演示数据,让深水页面首屏不空。
* 日期以"当前日期 +/- 偏移"动态生成,使到期分级预警在任何时间运行都有 即将到期/逾期 样本。
*/
@Component
@DemoSeed
@Order(6)
public class AdminQualSeeder implements CommandLineRunner {
@@ -6,7 +6,6 @@ import com.kaidi.oa.repository.ArchiveRepository;
import com.kaidi.oa.repository.ArchiveVersionRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -26,7 +25,7 @@ import java.time.Instant;
* <p>幂等:同 sourceType 在 archive 表已有记录则跳过整批(count 检查)。
* @Order(205) 在所有基础 Seeder 后运行。
*/
@Component
@DemoSeed
@Order(205)
public class ArchiveCrossModuleSeeder implements CommandLineRunner {
@@ -11,7 +11,6 @@ import com.kaidi.oa.repository.LegalRegulationRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -31,7 +30,7 @@ import java.time.temporal.ChronoUnit;
* 幂等:各表 count > 0 即跳过。
* @Order(202) 在 FertPkgAccountingSeeder(@Order 201) 之后运行。
*/
@Component
@DemoSeed
@Order(202)
public class AuditDeptSeeder implements ApplicationRunner {
@@ -5,7 +5,6 @@ import com.kaidi.oa.repository.BidTenderCollabRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -16,7 +15,7 @@ import java.time.Instant;
* 让前端看板活体可见且流程闭环可演示。
* 幂等:若已有数据则跳过,防止重启重复写入。
*/
@Component
@DemoSeed
@Order(215)
public class BidTenderCollabSeeder implements ApplicationRunner {
@@ -8,7 +8,6 @@ import com.kaidi.oa.repository.BidRepository;
import com.kaidi.oa.repository.BidTenderDocRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.LocalDate;
@@ -18,7 +17,7 @@ import java.time.LocalDate;
*
* @Order(215) 在所有主数据 Seeder 之后运行;幂等(count > 0 则跳过)。
*/
@Component
@DemoSeed
@Order(215)
public class BidTenderDocSeeder implements CommandLineRunner {
@@ -8,7 +8,6 @@ import com.kaidi.oa.repository.OrgBranchRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.ArrayList;
@@ -32,7 +31,7 @@ import java.util.List;
*
* @Order(15) 在 DataSeeder(@Order 1) 之后独立运行。
*/
@Component
@DemoSeed
@Order(15)
public class BranchFinDemoSeeder implements ApplicationRunner {
@@ -7,7 +7,6 @@ import com.kaidi.oa.repository.EmployeeShareMaterialRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.ArrayList;
@@ -21,7 +20,7 @@ import java.util.List;
*
* @Order(220) 在所有主数据 Seeder 之后运行;幂等(count > 0 则跳过)。
*/
@Component
@DemoSeed
@Order(220)
public class BrandCultureGapSeeder implements ApplicationRunner {
@@ -8,7 +8,6 @@ import com.kaidi.oa.repository.ContentTaskRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.ArrayList;
@@ -20,7 +19,7 @@ import java.util.List;
* 补全两个"活体返回空/无数据"缺口,让前端物料管理与任务分派功能可见可演示。
* @Order(22),在 DataSeeder(@Order 1) 之后运行,幂等(count > 0 跳过)。
*/
@Component
@DemoSeed
@Order(22)
public class BrandCultureSeeder implements ApplicationRunner {
@@ -12,7 +12,6 @@ import com.kaidi.oa.repository.StandardCostRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -37,7 +36,7 @@ import java.time.temporal.ChronoUnit;
* 幂等:各表 count() > 0 即跳过,重启不重复插入。
* @Order(9) 在 DataSeeder(@Order 1) 之后独立运行。
*/
@Component
@DemoSeed
@Order(9)
public class CostCtrlDemoSeeder implements ApplicationRunner {
@@ -20,7 +20,6 @@ import com.kaidi.oa.repository.RdEbomRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -50,7 +49,7 @@ import java.time.temporal.ChronoUnit;
*
* 幂等:各表 count() > 0 则跳过,重启安全。
*/
@Component
@DemoSeed
@Order(215)
public class CostCtrlGapSeeder implements ApplicationRunner {
@@ -8,7 +8,6 @@ import com.kaidi.oa.repository.CslTimesheetRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -21,7 +20,7 @@ import java.time.Instant;
*
* 幂等:按 code 查重后跳过,重启不重复插入。
*/
@Component
@DemoSeed
@Order(7)
public class CslDemoSeeder implements ApplicationRunner {
@@ -7,7 +7,6 @@ import com.kaidi.oa.repository.HrCultureKpiRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.ArrayList;
@@ -21,7 +20,7 @@ import java.util.List;
*
* @Order(225) 在 BrandCultureGapSeeder(@Order 220) 之后运行,幂等(count>0跳过)。
*/
@Component
@DemoSeed
@Order(225)
public class CultureBriefingKpiSeeder implements ApplicationRunner {
@@ -9,7 +9,6 @@ import com.kaidi.oa.repository.HrEmpSatisfactionSurveyRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.Instant;
@@ -28,7 +27,7 @@ import java.util.List;
* @Order(50),在 DataSeeder(@Order 1) 及 BrandCultureSeeder(@Order 22) 之后运行。
* 幂等:CultureGoal 有数据时跳过全部种子。
*/
@Component
@DemoSeed
@Order(50)
public class CultureProjectAssessSeeder implements ApplicationRunner {
@@ -158,7 +158,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
@@ -171,7 +170,7 @@ import java.util.List;
* in-flight form instances, plus org/users/roles, meetings, documents,
* announcements and schedule events.
*/
@Component
@DemoSeed
@Order(1)
public class DataSeeder implements CommandLineRunner {
@@ -7,7 +7,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -15,7 +14,7 @@ import java.time.Instant;
* 申报成果库演示数据补种(Gap 4:活体 decl-achievements 0条,功能可达但未预置演示数据)。
* Order(3)=在 DataSeeder(Order 1)/AdminQualSeeder 之后运行,避免依赖竞争。
*/
@Component
@DemoSeed
@Order(3)
public class DeclAchievementSeeder implements CommandLineRunner {
@@ -0,0 +1,18 @@
package com.kaidi.oa.seed;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@ConditionalOnProperty(name = "oa.seed.demo", havingValue = "true", matchIfMissing = true)
public @interface DemoSeed {
}
@@ -10,7 +10,6 @@ import com.kaidi.oa.repository.FertPkgMaterialLedgerRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -27,7 +26,7 @@ import java.time.Instant;
* 幂等:按唯一编码查重后跳过,重启不重复插入。
* Order(201) 保证在 DataSeeder(200) 之后执行。
*/
@Component
@DemoSeed
@Order(201)
public class FertPkgAccountingSeeder implements ApplicationRunner {
@@ -14,7 +14,6 @@ import com.kaidi.oa.repository.FinRdTimesheetRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -35,7 +34,7 @@ import java.time.temporal.ChronoUnit;
*
* @Order(13) 在 FinDeptSeeder(@Order 12) 之后运行。
*/
@Component
@DemoSeed
@Order(13)
public class FinDeptDeepSeeder implements ApplicationRunner {
@@ -8,7 +8,6 @@ import com.kaidi.oa.repository.TaxFilingRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -26,7 +25,7 @@ import java.time.temporal.ChronoUnit;
*
* @Order(215) 在既有 FinDeptSeeder(@Order 12)、BranchFinDemoSeeder(@Order 15) 之后。
*/
@Component
@DemoSeed
@Order(215)
public class FinDeptGapSeeder implements ApplicationRunner {
@@ -14,7 +14,6 @@ import com.kaidi.oa.repository.FinVoucherTemplateRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -35,7 +34,7 @@ import java.time.temporal.ChronoUnit;
*
* @Order(12) 在 DataSeeder(@Order 1) 之后运行。
*/
@Component
@DemoSeed
@Order(12)
public class FinDeptSeeder implements ApplicationRunner {
@@ -15,7 +15,6 @@ import com.kaidi.oa.repository.FinVoucherTemplateRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.Instant;
@@ -36,7 +35,7 @@ import java.util.List;
*
* @Order(20) DataSeeder(@Order 1) BranchFinDemoSeeder(@Order 15) 之后运行
*/
@Component
@DemoSeed
@Order(20)
public class FinPaymentDemoSeeder implements ApplicationRunner {
@@ -13,7 +13,6 @@ import com.kaidi.oa.repository.PmtBankRelationRepository;
import com.kaidi.oa.repository.RepaymentPlanRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.Instant;
@@ -24,7 +23,7 @@ import java.time.Instant;
* 只在对应表为空时执行防止重复@Order(20) DataSeeder(@Order(1)) 之后运行
*/
@Order(20)
@Component
@DemoSeed
public class FinancingDomainSeeder implements CommandLineRunner {
private final PmtBankRelationRepository bankRelRepo;
@@ -4,7 +4,6 @@ import com.kaidi.oa.domain.HrLaborTemplate;
import com.kaidi.oa.repository.HrLaborTemplateRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.LocalDate;
@@ -17,7 +16,7 @@ import java.time.LocalDate;
*
* 幂等count > 0 跳过@Order(226) AdminQualElecSeeder(225) 之后运行
*/
@Component
@DemoSeed
@Order(226)
public class HrLaborTemplateSeeder implements CommandLineRunner {
@@ -10,7 +10,6 @@ import com.kaidi.oa.repository.HrRecruitCandidateRepository;
import com.kaidi.oa.repository.HrRecruitJobRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.LocalDate;
@@ -25,7 +24,7 @@ import java.time.LocalDate;
* - HrInterviewQuestion 5 条面试题AI 生成标记
* - HrAiDocDraft 4 AI 生成文档入职资料/岗位说明/员工手册/转正考核多状态
*/
@Component
@DemoSeed
@Order(203)
public class HrRecruitSeeder implements CommandLineRunner {
@@ -15,7 +15,6 @@ import com.kaidi.oa.repository.ItSecPolicyConfigRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.Instant;
@@ -27,7 +26,7 @@ import java.time.ZoneOffset;
* @Order(204) 在所有主数据Seeder后运行幂等count > 0 则跳过
*/
@Order(204)
@Component
@DemoSeed
public class ItDeptDemoSeeder implements ApplicationRunner {
private final ItMonitorItemRepository monitorRepo;
@@ -6,7 +6,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -19,7 +18,7 @@ import java.time.temporal.ChronoUnit;
*
* @Order(205) 在所有主数据 Seeder 之后幂等count > 0 则跳过
*/
@Component
@DemoSeed
@Order(205)
public class LabElnAccessLogSeeder implements CommandLineRunner {
@@ -6,7 +6,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -16,7 +15,7 @@ import java.time.Instant;
* labkanban.vue 活体展示多状态卡片
* Order(8) = 在所有基础 Seeder 之后FtsIndexRunner(200) 之前
*/
@Component
@DemoSeed
@Order(8)
public class LabTestKanbanSeeder implements CommandLineRunner {
@@ -11,7 +11,6 @@ import com.kaidi.oa.repository.LegalCreditInvestRepository;
import com.kaidi.oa.repository.LitigationCaseRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -27,7 +26,7 @@ import java.time.Instant;
* @Order(20) DataSeeder(@Order 1) 之后运行幂等count>0 则跳过
*/
@Order(20)
@Component
@DemoSeed
public class LegalDemoSeeder implements CommandLineRunner {
private final LitigationCaseRepository caseRepo;
@@ -6,7 +6,6 @@ import com.kaidi.oa.repository.ComplianceAnonReportRepository;
import com.kaidi.oa.repository.LegalPolicyRevisionRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -17,7 +16,7 @@ import java.time.Instant;
* @Order(21) LegalDemoSeeder(@Order 20) 之后幂等
*/
@Order(21)
@Component
@DemoSeed
public class LegalPolicyAnonSeeder implements CommandLineRunner {
private final LegalPolicyRevisionRepository revisionRepo;
@@ -6,7 +6,6 @@ import com.kaidi.oa.repository.MarketCampaignRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.ArrayList;
@@ -23,7 +22,7 @@ import java.util.List;
*
* @Order(10) DataSeeder(@Order 1) 之后独立运行幂等count > 0 则跳过
*/
@Component
@DemoSeed
@Order(10)
public class MarketCampaignSeeder implements ApplicationRunner {
@@ -7,7 +7,6 @@ import com.kaidi.oa.repository.SysDeptRepository;
import com.kaidi.oa.repository.SysUserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@@ -23,7 +22,7 @@ import java.util.Map;
* Idempotent + non-destructive: runs after the base seeder, only adds users that are
* missing (guard on a sentinel login). All accounts use password 123456.
*/
@Component
@DemoSeed
@Order(2)
public class OrgEnrichmentSeeder implements CommandLineRunner {
@@ -7,7 +7,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -21,7 +20,7 @@ import java.time.Instant;
* 注意 seeder 独立于 DataSeederOrder=2 DataSeeder Order=1 之后运行
* 不修改 DataSeeder 的大构造函数
*/
@Component
@DemoSeed
@Order(2)
public class RdDeclPlanSeeder implements CommandLineRunner {
@@ -7,7 +7,6 @@ import com.kaidi.oa.repository.SewagePlantBenchmarkRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -23,7 +22,7 @@ import java.time.Instant;
*
* 幂等各表 count == 0 才插入
*/
@Component
@DemoSeed
@Order(206)
public class SewageBenchmarkSeeder implements ApplicationRunner {
@@ -8,7 +8,6 @@ import com.kaidi.oa.repository.SewageExtLabCommissionRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
@@ -22,7 +21,7 @@ import java.time.temporal.ChronoUnit;
*
* 幂等linkageRepo.count() == 0 才插入commRepo.count() == 0 才插入
*/
@Component
@DemoSeed
@Order(30)
public class SewageCrossDeptSeeder implements ApplicationRunner {
@@ -11,7 +11,6 @@ import com.kaidi.oa.repository.SupervisionProjectRepository;
import com.kaidi.oa.repository.SvIndepClaimRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.List;
@@ -32,7 +31,7 @@ import java.util.List;
* <p>@Order(210) DataSeeder(@Order 1) 之后运行幂等SvIndepClaim.count>0 则跳过
*/
@Order(210)
@Component
@DemoSeed
public class SvSupervisionMinuteSeeder implements CommandLineRunner {
private final SvIndepClaimRepository claimRepo;
@@ -8,7 +8,6 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import java.io.InputStream;
@@ -21,7 +20,7 @@ import java.io.InputStream;
* JSON file under resources/seed-templates and rebuilding adds templates without
* any code change. This is how 凯迪科技's real approval processes are catalogued.
*/
@Component
@DemoSeed
@Order(100)
public class TemplateJsonSeeder implements CommandLineRunner {
@@ -10,7 +10,6 @@ import com.kaidi.oa.repository.WwMsdsFileRepository;
import com.kaidi.oa.repository.WwSamplingTaskRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.Instant;
@@ -24,7 +23,7 @@ import java.time.Instant;
* 4. WwSamplingTask废水采样任务演示数据覆盖不同状态和超标场景 Gap-11 桥接演示
*/
@Order(9)
@Component
@DemoSeed
public class WwOpsSeeder implements CommandLineRunner {
private final WwMsdsFileRepository msdsRepo;
@@ -1,19 +1,41 @@
package com.kaidi.oa.web;
import com.kaidi.oa.common.ApiResp;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Map;
/** Liveness probe. */
/** Readiness probe used by the installer and online-update rollback gate. */
@RestController
@RequestMapping("/api/oa")
public class HealthController {
private final DataSource dataSource;
public HealthController(DataSource dataSource) {
this.dataSource = dataSource;
}
@GetMapping("/health")
public ApiResp<Map<String, String>> health() {
return ApiResp.ok(Map.of("status", "UP"));
public ResponseEntity<ApiResp<Map<String, String>>> health() {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("SELECT 1")) {
if (connection.isValid(2) && result.next() && result.getInt(1) == 1) {
return ResponseEntity.ok(ApiResp.ok(Map.of("status", "UP", "database", "UP")));
}
} catch (Exception ignored) {
// Health responses deliberately avoid exposing connection details.
}
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(new ApiResp<>(50301, "database unavailable", Map.of("status", "DOWN", "database", "DOWN")));
}
}