spring: application: name: parking-server profiles: active: local # ========== 引入「存量业务参数」文件 ========== # 老工程按模块各有一份 application*.properties,红线第 9 条禁止改、importer 也故意不搬; # 但存量代码里有 37 个 `@Value("${...}")` 是业务参数(不是连接串),不补就启动失败。 # 这一批集中收在 legacy-params.properties(由 tools/p2_audit_placeholders.py 捞出并核对)。 # ⚠️ 优先级:application-{profile}.yaml(profile 专属)> 本文件 > 被 import 的文件, # 所以按环境覆盖这些参数时,写进 application-{profile}.yaml 即可。 config: import: classpath:legacy-params.properties # ========== Boot 1.5 → 2.x 的默认值翻转(必配,不是可选优化)========== # 老工程是 Spring Boot 1.5.2,那时 spring.main.allow-circular-references 默认 true; # 2.6 起默认 false,启动即抛 # "The dependencies of some of the beans in the application context form a cycle"。 # 存量代码是 2000+ 个 ServiceImpl 之间靠字段 @Autowired 互引,环是网状的 # (实测:DealGeoInOutParkDataSchedule → GeoInOutParkReportInnerServiceImpl → BerthsDaoImpl # ⇄ ParkFreeBerthBizServiceImpl),且老代码里没有一处用 @Lazy / setter 注入规避。 # 拆环 = 改动存量业务装配方式,触碰项目红线第 6 条「禁止擅自重构业务逻辑」,故显式放行。 main: allow-circular-references: true # ⚠️ 这里**故意不写** allow-bean-definition-overriding(2.1 起默认 false,保持即可): # 它是「两个**不同类**撞同一个 bean 名」的唯一探针,本次重构正是靠启动报错逐个揪出 # 9 组真实冲突并补上显式 bean 名修掉的。为封闭 jar 里 ChargeConfig ↔ ChargeService # 那一组开全局放水,等于把最该发现的错误静默掉;那组已改用「精确排除」处理, # 见 ParkingServerApplication 类注释与 AGENTS.md 坑 13/14。 # 资源信息:文件上传大小限制 servlet: multipart: max-file-size: 20MB max-request-size: 20MB # ========== 邮件(存量 pay/park 的 MailService,发票/异常告警用)========== # 老工程把 spring.mail.* 写在**各模块的 application.properties** 里 # (zteits-pay / zteits-park 各一份,7 个 profile 各一组,值逐字相同), # 红线第 9 条禁止改那些文件、importer 也故意不搬 —— 单 Jar 只有一份配置, # 所以在这里补。四个键缺一不可: # host —— MailSenderAutoConfiguration 的 @ConditionalOnProperty(prefix="spring.mail", name="host"); # 缺 host 就没有 JavaMailSender Bean,而 MailService 是裸 @Autowired JavaMailSender, # 启动即报 `No qualifying bean of type 'org.springframework.mail.javamail.JavaMailSender'` # (实测装配链:mailService → field 'sender')。 # username —— 两个 MailService 都用 @Value("${spring.mail.username}") **且没有默认值**, # 缺失是**占位符解析失败**,比缺 Bean 更早炸。 # password —— 只在真正发信时用(Boot 的 test-connection 默认 false,启动不会去连 SMTP); # **真实密码不进仓库**(红线 11),生产用 SPRING_MAIL_PASSWORD 注入, # 待轮换的旧密码见 docs/密钥轮换清单.md。 # 默认 host/port 取老工程原值(smtp.163.com:25,不是密钥);默认 username/password 留空, # 于是「能启动、发不出信、发信时以 auth 失败**报错**(而不是静默)」—— 这是没有凭据时的预期状态。 mail: host: ${SPRING_MAIL_HOST:smtp.163.com} port: ${SPRING_MAIL_PORT:25} username: ${SPRING_MAIL_USERNAME:} password: ${SPRING_MAIL_PASSWORD:} # Jackson 全局配置 jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER # 未匹配到 Controller 时抛 NoHandlerFoundException,由 GlobalExceptionHandler 统一转成 # CommonResult(404) 的 JSON 兜底(未迁移的模块就是这种「路由不存在」的状态)。 # 否则前端拿到的是 Spring Boot 默认 HTML 错误页,无法按 code 处理。 throw-exception-if-no-handler-found: true web: resources: # 关闭默认静态资源映射,配合上面的开关;knife4j / springdoc 的 /doc.html、/v3/api-docs # 由它们自己注册的 ResourceHandler 提供,与本开关无关(已实测仍可访问)。 add-mappings: false # 定时任务(Quartz):P0 用内存存储,P1 起改为数据库存储 quartz: job-store-type: memory properties: org: quartz: scheduler: instance-id: AUTO instance-name: parkingScheduler threadPool: threadCount: 20 # 消息队列(Redis 实现)默认关闭外部 MQ rabbitmq: listener: simple: auto-startup: false server: port: 48080 servlet: encoding: charset: UTF-8 force: true # ========== 项目业务配置 ========== parking: info: version: 1.0.0 base-package: com.zteits.parking # 注:P0 的骨架占位开关 `parking.p0.enabled` 已随 P0StubConfiguration 一起删除。 # P1 起 OAuth2TokenCommonApi / PermissionCommonApi / DictDataCommonApi 三个跨模块契约 # 均由 parking-module-system 的 OAuth2TokenServiceImpl / PermissionServiceImpl / # DictDataServiceImpl 提供(三者分别继承了对应契约接口)。 web: admin-api: prefix: /admin-api controller: '**.controller.admin.**' app-api: prefix: /app-api controller: '**.controller.app.**' admin-ui: url: http://localhost:5173 security: token-header: Authorization token-parameter: token mock-enable: false mock-secret: test password-encoder-length: 4 permit-all-urls: - /actuator/** - /doc.html - /swagger-ui/** - /swagger-ui.html - /v3/api-docs/** - /webjars/** - /favicon.ico swagger: title: ZTEITS 智能停车云平台 description: 停车云平台管理后台接口文档(模块化单体,单 Jar 发布) author: ZIEITS version: 1.0.0 url: https://www.zteits.com email: dev@zteits.com license: MIT license-url: https://opensource.org/licenses/MIT cache: redis-scan-batch-size: 30 # ========== 刷新缓存共享密钥(老门户 RefreshCacheController 4 条 @NoAuth)========== # 老代码写死 32 位共享密钥,迁移时按 AGENTS.md「批 6 第三十四切片」④ (B) 改配置化(红线 11)。 # 代码里**零密钥、无默认值**;这里默认取环境变量、缺省为空 ⇒ 任何环境不配即 fail-closed # (一律回 "token不正确")。本地测试值在 application-local.yaml 覆盖;生产用 # PARKING_REFRESH_CACHE_TOKEN 或 --parking.refresh-cache.token=... 配成现网原串。 refresh-cache: token: ${REFRESH_CACHE_TOKEN:} # ========== 老新组织 id 换算表(P3 组织口径桥,批 6 第四十七切片)========== # 新 `system_org.id` 与存量业务表里存的 `sys_org.id` 是两个空间(坑 22)。 # `LoginUserBridge` 用它把登录人的 orgId 换算成老 id 再交给存量服务;格式 `新id:老id,...`。 # **默认空 ⇒ 恒等映射**,任何环境不配都与改造前完全一致(配置在 profile/环境变量里给)。 # ========== 老新操作员 id 换算表(P3 ②,第一百六十三切片)========== # 新 `system_users.id` 与存量审计列(create_op/modify_op/create_empid/s_op_id)里的 # `sys_op.id` 是两个空间。`LoginUserBridge` 用它把 opId 换算成老操作员 id; # 格式 `新id:老id,...`。**默认空 ⇒ 恒等映射**,任何环境不配都与改造前完全一致。 legacy: org-id-mapping: ${LEGACY_ORG_ID_MAPPING:} op-id-mapping: ${LEGACY_OP_ID_MAPPING:} # ========== 企业微信(存量 zteits-wxcp-portal 的告警发送)========== # 随迁进 parking-module-system 的 com.zteits.wxcp.* 用 @ConfigurationProperties(wechat.cp) 绑定。 # ⚠️ 这两个键**必须存在**:WxCpConfiguration.initServices() 是 @PostConstruct, # 里面直接 properties.getAppConfigs().stream() —— appConfigs 为 null 会启动即 NPE。 # 这里给**空列表**是刻意的:空列表能正常绑定(不执行任何 lambda), # 于是没有企业微信凭据的环境也能启动,只是真的要走告警时 # WxCpConfiguration.getCpService(agentId) 返回 null(外部集成未配置)。 # 生产/测试环境在此之上覆盖(凭据本身见 docs/密钥轮换清单.md,不进仓库): # wechat: # cp: # corp-id: ww... # app-configs: # - agent-id: 1000002 # secret: ${WECHAT_CP_SECRET_1000002:} # token: ${WECHAT_CP_TOKEN_1000002:} # aes-key: ${WECHAT_CP_AESKEY_1000002:} wechat: cp: corp-id: ${WECHAT_CP_CORP_ID:} app-configs: [] # ========== SpringDoc / Knife4j 接口文档 ========== springdoc: api-docs: enabled: true path: /v3/api-docs swagger-ui: path: /swagger-ui knife4j: enable: true setting: language: zh_cn # ========== MyBatis-Plus 配置 ========== # 核心:存量 mapper XML 与新建的 MyBatis-Plus 共用同一个 SqlSessionFactory, # mapper-locations 一条通配同时命中「新代码 Mapper XML」与「存量 mybatis/mapper|smapper」。 mybatis-plus: mapper-locations: - classpath*:mapper/**/*.xml - classpath*:mybatis/mapper/**/*.xml - classpath*:mybatis/smapper/**/*.xml type-aliases-package: com.zteits.parking.module.*.dal.dataobject,com.zteits.parking.module.*.domain global-config: banner: false db-config: id-type: AUTO # ⚠️ 存量六个库**没有 del_flag 列**(已核验:0 处),因此这里不配置全局逻辑删除。 # 详见 parking-spring-boot-starter-mybatis 的 BaseDO 类注释。 table-underline: true configuration: map-underscore-to-camel-case: true cache-enabled: false call-setters-on-nulls: true jdbc-type-for-null: 'null' # ↓↓↓ 以下 3 项是**照搬老工程** mybatis-plugin.xml 的 settings # (zteits-{park,pay,bill}/src/main/resources/mybatis/mybatis-plugin.xml,三份逐字相同)。 # 老工程走的是自己 new SqlSessionFactoryBean 的路线,这些 settings 写在 XML 里; # 新工程的 SqlSessionFactory 由 MyBatis-Plus 建,所以要把**非默认值**的挑出来补在这里。 # 逐项对照过 10 条 setting,只有下面 3 条与 MyBatis 默认值不同: # mapUnderscoreToCamelCase=true —— 已在上方 # cacheEnabled=false —— 已在上方 # localCacheScope=STATEMENT —— MyBatis 默认 SESSION,**必须补** # useGeneratedKeys=true —— MyBatis 默认 **false**,**必须补** # defaultStatementTimeout=60 —— 默认 null(不超时),**必须补** # lazyLoadingEnabled / multipleResultSetsEnabled / useColumnLabel / # autoMappingBehavior / defaultExecutorType —— 与默认值一致,不必写 local-cache-scope: statement default-statement-timeout: 60 use-generated-keys: true # ========== PageHelper(仅存量代码搬迁时按需使用,新代码禁止)========== pagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true # ⚠️ `page-size-zero: true` 是**必须**的,不是可选项:它决定 `startPage(n, 0)` 是 # 「查全部」还是「查空」,而存量代码里 60+ 个导出端点全都靠 `new BaseInfo(1, 0)` # 表达「不分页、导全部」。 # # 老栈 PageHelper 5.0.0:`startPage(1, 0)` → LIMIT **不拼** → 全量返回(total=1, list.size=1) # 新栈 PageHelper 6.1.1(`page-size-zero` 默认 false):拼 `LIMIT 0` → **0 行**(total=1, list.size=0) # 实测证据与复现脚本见 AGENTS.md「pageSize=0:老栈查全部、新栈查空」一节 # (/tmp/pgprobe 的两版本对照实验)。不设这一项,所有导出都会**静默导出空表**。 # # 老工程 application.properties 里没写这一项 —— 那不是"老栈也不需要",而是 5.0.0 里 # pageSize=0 本来就不拼 LIMIT,`pageSizeZero` 只管 RowBounds 那条路径。换了 PageHelper # 大版本后语义变了,必须在**新栈**显式补回来。 page-size-zero: true # 老工程 application.properties 里还有 `pagehelper.dialect=com.github.pagehelper.PageHelper` # 与 `pagehelper.params=countSql`:前者是 5.x 的自定义 dialect 类全名(6.x 已废弃该配置形态), # 后者是 supportMethodsArguments 的默认值,二者都无迁移必要,故不搬。 # ========== Actuator 健康检查 ========== management: endpoints: web: base-path: /actuator exposure: include: '*' endpoint: health: show-details: always # ========== 日志 ========== logging: file: name: ./logs/${spring.application.name}/${spring.application.name}.log