feat: 邀请函邮件发送 + 首次设密/密码为空 + 姓名单一可信源

邀请发送 (菜单暂隐藏, 待确认参数见 发送邀请-待确认参数.md):
- 后端: BizInvite / BizInviteRecipient + Controller/Service/Mapper/XML
- 邮件: InviteMailSender (spring-boot-starter-mail SMTP) + application*.yml 邮件配置
- 上传进度: UploadProgressRegistry + UploadProgressController
- 前端: InviteList / InviteNew / InviteDetail / InviteView + api/business/invite.js
- 原型: proto/html/components/invite-detail / new-invitation / send-invitation

登录/账号:
- 首次设密: /getInfo 返回 isPasswordEmpty, SysProfileController 密码为空时跳过旧密码校验, ForcePasswordDialog 强制弹窗
- 姓名单一可信源 resolveDisplayName: doctor→biz_expert.name, sponsor/executor→biz_person.name, 其余回退 nick_name
- OA compliance 门禁改为按手机号查 ecology 视图 (不再限定 manager/leader)

其它:
- OSS zip 在线查看 (列清单+取单文件, 公开只读) + SecurityConfig permitAll
- doctor 项目详情 ProjectDetail.vue
- 数据库/测试/设计文档 (md) 入库
This commit is contained in:
郭庆泰
2026-09-10 20:40:49 +08:00
parent 56615f9806
commit 5a0a892574
171 changed files with 11032 additions and 876 deletions
@@ -26,7 +26,7 @@ public class FileUploadUtils
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
public static final long DEFAULT_MAX_SIZE = 200 * 1024 * 1024L;
/**
* 默认的文件名最大长度 100
@@ -4,13 +4,13 @@ import java.lang.reflect.Method;
/**
* 雪花 ID 注入工具 - 业务 entity insert 前若主键为空则自动填雪花 ID.
* 约定: 主键 getter/setter 命名 must be getXxxId()/setXxxId(String) (BizXxxId 风格).
* 约定: 主键 getter/setter 命名 getXxxId()/setXxxId(...), 支持 String 或 Long 主键 (BizXxxId 风格).
*/
public class SnowflakeId {
/**
* 主键为空时, 雪花 ID 注入 id field (支持 String 类型主键).
* 通过反射调用 setXxxId(String.valueOf(IdGenerator.generateId())).
* 主键为空时, 雪花 ID 注入 id field (支持 String / Long 类型主键).
* 通过反射调用 setXxxId(String.valueOf(IdGenerator.generateId())) 或 setXxxId(Long).
*
* @param entity 业务对象
* @param idFieldName 主键字段名, 如 "projectId" / "annId" / "id" (注意 BizMeetingSettlement 是 "id")
@@ -23,9 +23,15 @@ public class SnowflakeId {
Method getM = entity.getClass().getMethod(getter);
Object current = getM.invoke(entity);
if (current == null || (current instanceof String && ((String) current).isEmpty())) {
String snowId = String.valueOf(IdGenerator.generateId());
Method setM = entity.getClass().getMethod(setter, String.class);
setM.invoke(entity, snowId);
long snowId = IdGenerator.generateId();
// 优先 String setter (老约定 setXxxId(String)), 其次 Long (setXxxId(Long), 如 BizProject.projectId)
try {
Method setM = entity.getClass().getMethod(setter, String.class);
setM.invoke(entity, String.valueOf(snowId));
} catch (NoSuchMethodException e) {
Method setM = entity.getClass().getMethod(setter, Long.class);
setM.invoke(entity, snowId);
}
}
} catch (NoSuchMethodException e) {
// 字段可能叫别的, 跳过