Files
form-center-service/src/main/resources/application.yml
T
chenxing bff0fb15fa
build / build-push (push) Successful in 52s
feat(answer): T3 后端·答卷核心——提交/幂等/限次/索引/软删留痕 + 平台标准版上传
答卷域是"校验只写一遍、索引只有一份"这条选型(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
2026-10-14 16:50:07 +08:00

126 lines
6.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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