feat: 会议批量审核 + 公示按发布时间倒序 + 现场照片 dialog 预览 + 重复报错精确

- 会议列表 manager 批量合规审核 (批量通过/退回, best-effort 逐条回执)
- 公示列表按 publish_time 倒序, 发布/开通写本地 publish_time (修 toISOString UTC 时区 bug)
- 签到表/前全景/后全景「查看照片」改 dialog 预览 (复用 Preview.vue)
- 手机号/用户名重复报错精确提示 (后端 fieldMap + 前端 duplicateTip)
- 医生首页欢迎语改「欢迎使用合规系统」
This commit is contained in:
郭庆泰
2026-08-26 00:41:33 +08:00
parent ea2024d083
commit b5b3e3a213
15 changed files with 387 additions and 115 deletions
@@ -1,7 +1,9 @@
package com.ruoyi.business.controller;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ruoyi.business.domain.BizMeetingMaterial;
@@ -283,22 +285,72 @@ public class BizMeetingController extends BaseController {
@Log(title = "会议审核", businessType = BusinessType.UPDATE)
@PostMapping("/{meetingId}/audit-compliance")
public AjaxResult auditCompliance(@PathVariable("meetingId") Long meetingId, @RequestBody AuditBody body) {
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
if (!"manager".equals(roleType)) throw new ServiceException("仅合规人员可操作");
if (!"manager".equals(SecurityUtils.getLoginUser().getUser().getRoleType())) {
throw new ServiceException("仅合规人员可操作");
}
BizMeeting m = bizMeetingService.getById(meetingId);
if (m == null) throw new ServiceException("会议不存在");
boolean approved = Boolean.TRUE.equals(body.getApproved());
if (!approved && (body.getOpinion() == null || body.getOpinion().isEmpty())) {
throw new ServiceException("拒绝时意见不能为空");
}
return success(doComplianceAudit(m, approved, body.getOpinion()));
}
/**
* 批量合规审核 (role_type=manager): 一次对多个会议执行材料一级审核.
* <p>
* body: { "meetingIds": [..], "approved": true|false, "opinion": "..." }
* <p>best-effort: 逐条审核, 状态不符的会议跳过并回执失败原因, 不整批回滚.
*/
@Log(title = "会议审核", businessType = BusinessType.UPDATE)
@PostMapping("/batch-audit-compliance")
public AjaxResult batchAuditCompliance(@RequestBody BatchAuditBody body) {
if (!"manager".equals(SecurityUtils.getLoginUser().getUser().getRoleType())) {
throw new ServiceException("仅合规人员可操作");
}
List<Long> meetingIds = body.getMeetingIds();
if (meetingIds == null || meetingIds.isEmpty()) {
throw new ServiceException("请选择会议");
}
boolean approved = Boolean.TRUE.equals(body.getApproved());
if (!approved && (body.getOpinion() == null || body.getOpinion().isEmpty())) {
throw new ServiceException("拒绝时意见不能为空");
}
List<Long> successIds = new ArrayList<>();
List<Map<String, Object>> failures = new ArrayList<>();
for (Long meetingId : meetingIds) {
if (meetingId == null) continue;
try {
BizMeeting m = bizMeetingService.getById(meetingId);
if (m == null) throw new ServiceException("会议不存在");
doComplianceAudit(m, approved, body.getOpinion());
successIds.add(meetingId);
} catch (ServiceException e) {
Map<String, Object> f = new HashMap<>();
f.put("meetingId", meetingId);
f.put("reason", e.getMessage());
failures.add(f);
}
}
Map<String, Object> data = new HashMap<>();
data.put("total", meetingIds.size());
data.put("successCount", successIds.size());
data.put("failCount", failures.size());
data.put("failures", failures);
return success(data);
}
/**
* 内部: 合规审核核心流转 (阶段校验 + 状态写入 + audit_log). 单条/批量共用.
*/
private String doComplianceAudit(BizMeeting m, boolean approved, String opinion) {
Integer compliance = m.getMaterialComplianceApproved();
if (!"SUBMITTED".equals(m.getMaterialAuditStage()) || (compliance != null && compliance == 1)) {
throw new ServiceException("当前阶段不允许合规审核");
}
String result = approved ? "APPROVED" : "REJECTED";
if (approved) {
m.setMaterialComplianceApproved(1);
@@ -310,8 +362,8 @@ public class BizMeetingController extends BaseController {
m.setMaterialAuditTime(new Date());
m.setCurrentStage(stageDeriver.derivePhysicalStage(m));
bizMeetingService.updateByPrimaryKey(m);
appendAuditLog(m, "MATERIAL", result, body.getOpinion());
return success(result);
appendAuditLog(m, "MATERIAL", result, opinion);
return result;
}
/**
@@ -533,4 +585,17 @@ public class BizMeetingController extends BaseController {
public void setOpinion(String opinion) { this.opinion = opinion; }
}
/** request body for batch audit endpoint */
public static class BatchAuditBody {
private List<Long> meetingIds;
private Boolean approved; // true=通过 false=拒绝
private String opinion; // 意见
public List<Long> getMeetingIds() { return meetingIds; }
public void setMeetingIds(List<Long> meetingIds) { this.meetingIds = meetingIds; }
public Boolean getApproved() { return approved; }
public void setApproved(Boolean approved) { this.approved = approved; }
public String getOpinion() { return opinion; }
public void setOpinion(String opinion) { this.opinion = opinion; }
}
}
@@ -82,9 +82,8 @@ public class BizPublicController extends BaseController {
@GetMapping("/announcements")
public AjaxResult announcements() {
BizProject query = new BizProject();
query.setIsPublished("1");
List<BizProject> list = projectService.selectList(query);
// 公示列表按发布时间倒序 (后端排序), 只返回已发布项目
List<BizProject> list = projectService.selectPublicAnnouncements();
return success(list);
}
@@ -9,6 +9,8 @@ public interface BizProjectMapper
{
BizProject selectByPrimaryKey(Long projectId);
List<BizProject> selectList(BizProject entity);
/** 公开门户公示列表: is_published='1' 且未删除, 按 publish_time 倒序 (最新在前, 空值排最后) */
List<BizProject> selectPublicAnnouncements();
/** sponsor 端专属: projectIds + LEFT JOIN 当前 login 用户评分, 用于评分回显 */
List<BizProject> selectSponsorList(BizProject entity);
/** executor 端专属: JOIN biz_project_assign 过滤, 只返回分给当前 user 的项目 */
@@ -10,6 +10,8 @@ public interface IBizProjectService
{
BizProject getById(Long projectId);
List<BizProject> selectList(BizProject entity);
/** 公开门户公示列表: 已发布, 按发布时间倒序 */
List<BizProject> selectPublicAnnouncements();
/** sponsor 端专属: LEFT JOIN 当前 login 用户评分回显 */
List<BizProject> selectSponsorList(BizProject entity);
/** executor 端专属: JOIN biz_project_assign 过滤, 只返回分给当前 user 的项目 */
@@ -43,6 +43,9 @@ public class BizProjectServiceImpl implements IBizProjectService
public List<BizProject> selectList(BizProject entity)
{ return bizProjectMapper.selectList(entity); }
@Override
public List<BizProject> selectPublicAnnouncements()
{ return bizProjectMapper.selectPublicAnnouncements(); }
@Override
public List<BizProject> selectSponsorList(BizProject entity)
{ return bizProjectMapper.selectSponsorList(entity); }
@Override
@@ -28,6 +28,7 @@
<result property="isFinished" column="is_finished" />
<result property="isSettled" column="is_settled" />
<result property="isPublished" column="is_published" />
<result property="publishTime" column="publish_time" />
<result property="createBy" column="create_by" />
<result property="createUserId" column="create_user_id" />
<result property="createUserName" column="create_user_name" />
@@ -56,7 +57,7 @@
(p.total_amount - ifnull(p.manage_fee, 0)
- (select ifnull(sum(m.labor_fee), 0) from biz_meeting m where m.project_id = p.project_id and m.is_settled = 1 and m.is_deleted = 0)
- (select ifnull(sum(m.meeting_fee), 0) from biz_meeting m where m.project_id = p.project_id and m.is_settled = 1 and m.is_deleted = 0)) as available_amount,
p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, p.is_published, p.sponsor_org_id, p.lead_user_id, p.is_bid_project, p.create_by, p.create_user_id, p.create_time, p.update_by, p.update_time, p.manage_fee, p.role_labor, p.start_time, p.end_time, p.submit_deadline_days, p.support_contract_url, p.execute_contract_url, p.invitation_url, p.support_letter_url, p.publish_url, p.schedule_url, p.open_deadline, p.open_status, p.is_deleted,
p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, p.is_published, p.publish_time, p.sponsor_org_id, p.lead_user_id, p.is_bid_project, p.create_by, p.create_user_id, p.create_time, p.update_by, p.update_time, p.manage_fee, p.role_labor, p.start_time, p.end_time, p.submit_deadline_days, p.support_contract_url, p.execute_contract_url, p.invitation_url, p.support_letter_url, p.publish_url, p.schedule_url, p.open_deadline, p.open_status, p.is_deleted,
o.org_name as sponsor_org_name,
su.user_name as sponsor_admin_user_name,
lu.user_name as lead_user_name,
@@ -81,7 +82,7 @@
(p.total_amount - ifnull(p.manage_fee, 0)
- (select ifnull(sum(m.labor_fee), 0) from biz_meeting m where m.project_id = p.project_id and m.is_settled = 1 and m.is_deleted = 0)
- (select ifnull(sum(m.meeting_fee), 0) from biz_meeting m where m.project_id = p.project_id and m.is_settled = 1 and m.is_deleted = 0)) as available_amount,
p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, p.is_published,
p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, p.is_published, p.publish_time,
p.sponsor_org_id, p.lead_user_id, p.is_bid_project,
p.create_by, p.create_user_id, p.create_time, p.update_by, p.update_time, p.manage_fee, p.role_labor,
p.start_time, p.end_time, p.submit_deadline_days,
@@ -356,6 +357,14 @@
</where>
order by project_id desc
</select>
<!-- 公开门户公示列表: is_published='1' 且未删除, 按 publish_time 倒序 (最新发布在前, publish_time 为空排最后) -->
<select id="selectPublicAnnouncements" resultMap="BizProjectResult">
<include refid="selectFields"/>
where p.is_deleted = 0 and p.is_published = '1'
order by p.publish_time desc, p.project_id desc
</select>
<insert id="insert" parameterType="BizProject">
insert into biz_project
<trim prefix="(" suffix=")" suffixOverrides=",">
@@ -372,6 +381,7 @@
<if test="isFinished != null and isFinished != ''">is_finished,</if>
<if test="isSettled != null and isSettled != ''">is_settled,</if>
<if test="isPublished != null and isPublished != ''">is_published,</if>
<if test="publishTime != null">publish_time,</if>
<if test="sponsorOrgId != null">sponsor_org_id,</if>
<if test="leadUserId != null">lead_user_id,</if>
<if test="isBidProject != null and isBidProject != ''">is_bid_project,</if>
@@ -411,6 +421,7 @@
<if test="isFinished != null and isFinished != ''">#{isFinished},</if>
<if test="isSettled != null and isSettled != ''">#{isSettled},</if>
<if test="isPublished != null and isPublished != ''">#{isPublished},</if>
<if test="publishTime != null">#{publishTime},</if>
<if test="sponsorOrgId != null">#{sponsorOrgId},</if>
<if test="leadUserId != null">#{leadUserId},</if>
<if test="isBidProject != null and isBidProject != ''">#{isBidProject},</if>
@@ -462,6 +473,7 @@
<if test="isFinished != null and isFinished != ''">is_finished = #{isFinished},</if>
<if test="isSettled != null and isSettled != ''">is_settled = #{isSettled},</if>
<if test="isPublished != null and isPublished != ''">is_published = #{isPublished},</if>
<if test="publishTime != null">publish_time = #{publishTime},</if>
<if test="sponsorOrgId != null">sponsor_org_id = #{sponsorOrgId},</if>
<if test="leadUserId != null">lead_user_id = #{leadUserId},</if>
<if test="isBidProject != null and isBidProject != ''">is_bid_project = #{isBidProject},</if>
@@ -184,6 +184,7 @@ public class GlobalExceptionHandler
fieldMap.put("expert_id_card", "身份证号");
fieldMap.put("unit_name", "单位名称");
fieldMap.put("org_name", "机构名称");
fieldMap.put("person_phone", "手机号");
if (key != null && key.startsWith("uk_")) {
String field = key.substring(3);