build / build-push (push) Successful in 52s
答卷域是"校验只写一遍、索引只有一份"这条选型(D2 模型 2b)的兑现处,本轮把它一次落齐。
- 提交链路按语义排死顺序:钉版解析→截止→幂等命中→subject 授权→值校验→附件同校归属
→fill_quota 占位→落 answer→写索引。幂等必须在限次之前(重放那条本就占着名额),
限次不做先 count 再插而是事务内撞 uk 即拒(并发下 count 靠不住,方案 §5.4)
- 钉版与定义域共用同一条 resolve 遮蔽实现;升版窗口旧版仍可填并钉旧版(P20),
请求体不含 version/schId/submitterId——身份结构性不可注入(V17)
- FormValueValidator 按契约 §3 逐类校验值形状:date 严格 YYYY-MM-DD 且必须是真实日期、
number 空值只认 null、选项值取内联 ∪ allowedValues(含软删条目)、
附件只认 {id,fileName,key} 且拒绝对 URL/跨目录 key
- answer_index 只写显式 searchable 字段、多值一行一个、v_str 截 191;编辑按钉住版本重建,
软删同步删行且释放 fill_quota 名额;留痕只增不改,改状态无任何转移规则校验(V15)
- 上传模块自写平台标准版:FILE_PATH 三层兜底链、随机存储名、平台 15 项黑名单+svg、
≤50MB(并按 P12 显式配 spring.servlet.multipart,默认 1MB 会挡掉合法附件)
- 数据面刻意不建列表/详情/统计/导出读接口:四条读出路径必须共用 T4 的 scope 构造器,
现在写一个无范围过滤的详情接口就是给 T4 留后门
- T2 已验收文件仅按裁决改动:FormAdminGuard 收紧为 {2,7}(P21)并加 canManage;
FormDefService 抽出 resolvePublished/新增 pinnedForData(数据面溯源含软删表单);
FormRepository 加数据面取域查询;application.yml 只加 multipart 两行
自验:mvn -B -o clean package 绿(121 测试,本轮新增 54 条含 39 条打真实 MySQL,
V32 并发面用双线程非回滚 IT 取证),逐 V 证据与问题单 P23~P32 见 docs/T3-report.md
126 lines
6.6 KiB
YAML
126 lines
6.6 KiB
YAML
server:
|
||
port: 8086
|
||
# 本地无 context-path(同 flow-center dev / Notice dev);生产经 CONTEXT_PATH=/form-center 注入,
|
||
# Caddy 只反代 /form-center/api/* 与 /form-center/oapi/*,页面走前端静态。
|
||
# 端口/context-path 一律不写生产值:projman 用 SERVER_PORT、CONTEXT_PATH 环境变量注入(env 压过本文件)。
|
||
|
||
spring:
|
||
application:
|
||
name: form-center
|
||
datasource:
|
||
# 本地开发库;生产 DB 连接由 projman 平台供给(SPRING_DATASOURCE_* env 注入,本文件值被压过)
|
||
url: jdbc:mysql://localhost:3306/bsch_formcenter?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||
username: root
|
||
password: 123456
|
||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||
hikari:
|
||
minimum-idle: 5
|
||
maximum-pool-size: 20
|
||
jpa:
|
||
hibernate:
|
||
# 开发期 update 自动建表;生产环境建议 none + 升级脚本(sql/ 目录,见 T2 起)
|
||
ddl-auto: update
|
||
show-sql: false
|
||
open-in-view: false
|
||
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
|
||
properties:
|
||
hibernate:
|
||
jdbc:
|
||
time_zone: Asia/Shanghai
|
||
jackson:
|
||
date-format: yyyy-MM-dd HH:mm:ss
|
||
time-zone: Asia/Shanghai
|
||
# ===== 上传大小上限(T3 上传模块,问题单 P12 记档项)=====
|
||
# 必须显式配:Spring Boot 默认 max-file-size=1MB,会把契约 §3 的 ≤50MB 附件在校验器之前就挡掉
|
||
# (报 MultipartException,用户只看到"上传失败")。两处上限与方案 §7.7 全局兜底同口径:
|
||
# 单文件 ≤50MB;请求侧同样 50MB——前端逐文件上传(view-ui-plus 默认一次请求一个文件),
|
||
# 不给"一个请求塞多个大文件"的放大面。生产若需调整由平台 env 覆盖,不写进 jar。
|
||
servlet:
|
||
multipart:
|
||
max-file-size: 50MB
|
||
max-request-size: 50MB
|
||
|
||
# ===== 文件上传存储路径 =====
|
||
# 教训注释自 notice/service application.yml 原样抄入(2026-10 事故):
|
||
# 仅供本地 Windows 开发兜底;生产读 FILE_PATH(平台统一,共享配置 application-platform.yml 一份
|
||
# /data/upload,子应用不再各自维护)。
|
||
# 切勿在生产只依赖本键:历史教训是 Linux 容器把 D:/data/upload
|
||
# 锚定成 /app/D:/data/upload 写进容器可写层,每次更新部署附件全部丢失(2026-10)。
|
||
# 读取形态(T3 上传模块实现):${FILE_PATH:${FILE_UPLOAD_PATH:${file.upload-path:/data/upload}}}
|
||
# —— FILE_PATH 由 projman 注入且持久卷固定挂 /data/upload,本键只在不注入 env 的本地生效,不可删。
|
||
file:
|
||
upload-path: D:/data/upload
|
||
|
||
# ===== 基础平台 yh 配置(flow-center/Notice/svc-hall 同款三件套)=====
|
||
yh:
|
||
auth:
|
||
enabled: true
|
||
sso:
|
||
# 本地全链测试:指向 local-debug 的 CAS(8443 根路径,RSA+AJAX 登录页);生产由平台注入
|
||
# SSO_SERVER_URL_PREFIX / SSO_CLIENT_HOST_URL 覆盖(projman 按平台设置现算,本文件不要写生产值)
|
||
server-url-prefix: http://localhost:8443
|
||
# 本地 dev 指向后端直连(脚本化登录 service=client-host-url+请求路径)
|
||
client-host-url: http://localhost:8086
|
||
allow-headers: all
|
||
allow-origins: all
|
||
# 用户面与管理面全在 /api/* 下;本应用无匿名面。
|
||
# 业务方调用走 /oapi/*(appKey 签名,RequireAppSignFilter 强制 401)
|
||
url-pattern: /api/*
|
||
app-auth:
|
||
enabled: true
|
||
# 本应用自身身份(客户端签名用)——UserBase 注册后分配(进白名单),占位值上线前必须替换,
|
||
# 生产由 projman 注入 YH_APP_AUTH_APP_KEY / APP_SECRET,禁止明文入库仓库。
|
||
# 本地占位 appKey=100021(svc-hall=100018 → flow-center=100019 → work-leave=100020 顺延);
|
||
# 联调时需在 UserBase(及对端 trusted-apps)侧同步此占位对。
|
||
# 注意:占位 secret 必须带引号且含字母——裸 32 个 0 会被 YAML 按八进制整数解析成 "0",SignUtil 校验直接 500
|
||
app-key: 100021
|
||
app-secret: "form-center-dev-secret-100021"
|
||
# 提供方强制验签(两级模型 §4.1):/oapi/** 无签名三件套即 401(starter 版 RequireAppSignFilter)。
|
||
# T1 暂无 /oapi 端点,声明保留(T4 对外读接口落地即用,不出现「先上线后端点无门」的空窗)
|
||
server:
|
||
enforce-patterns: [/oapi/*]
|
||
# 中央密钥链(两级模型 §4.2):trusted-apps 先(本地确定性兜底),未命中走 SignKeyResolver
|
||
# (Redis sign:app:* → Caffeine → UserBase 兜底端点)——任何在 UserBase 注册过的 appKey 自动可验
|
||
trust-registry: true
|
||
# 入站验签白名单:只列本地需要直调本应用 /oapi 的对端,联调时按实际补充(号段同各应用本地占位)。
|
||
# 开 trust-registry 后此表仍保留为主兜底(Redis 停摆时白名单确定性工作)
|
||
trusted-apps:
|
||
"100018": "svc-hall-dev-secret-100018"
|
||
"100019": "flow-center-dev-secret-100019"
|
||
open:
|
||
# 身份密钥唯一来源 = yh.app-auth(本文件 app-auth 段),open.* 副本不写;
|
||
# base-host 是「服务目录三级兜底」的输入(userbase 目标 = base-host + 枚举 ctx /userbase):
|
||
# 本地指向 local-debug 的 UserBase;生产由平台注入 YH_OPEN_BASE_HOST 覆盖(base.yunhaixingtong.com)。
|
||
# ⚠️ 绝不能把 services.userbase=http://localhost:8060 之类的本地覆盖写进本文件(同 flow-center 纪律):
|
||
# yh.open.services.{key} 是目录第 2 级,平台注入的 base-host 只喂第 3 级兜底、压不过它 ——
|
||
# 写进 jar 会让部署实例去连容器内的 localhost:8060(2026-09-18 事故:Connection refused →
|
||
# 所有 /api/* 报 code=6 内部服务错误)。本地联调请运行时注入:
|
||
# YH_OPEN_SERVICES_USERBASE=http://localhost:8060 mvn -o spring-boot:run
|
||
base-host: http://localhost:8060
|
||
base-path: ""
|
||
|
||
# ===== 表单中心专属配置 =====
|
||
form-center:
|
||
# 本地开发跨端口 CORS:本应用 front dev(8089) 直调 /api/* 需带凭据的 CORS 头
|
||
# (CAS cookie 不隔离端口,免二次登录成立的前提);同主域生产置空即整体关闭
|
||
cors:
|
||
allowed-origins: http://localhost:8089
|
||
|
||
logging:
|
||
level:
|
||
com.yunhai.formcenter: debug
|
||
|
||
management:
|
||
endpoints:
|
||
web:
|
||
exposure:
|
||
include: health
|
||
endpoint:
|
||
health:
|
||
show-details: never
|
||
health:
|
||
redis:
|
||
# 引 data-redis 仅服务于 trust-registry 密钥链;关健康指标防 Redis 停摆拉 DOWN /actuator/health
|
||
# (组合链 trusted-apps 先行兜底,Redis 不可用时验签仍确定性工作)
|
||
enabled: false
|