feat: 会议ID应用赋值 + 会议表单/详情优化 + admin工作台统计与用户管理
- meetingId 由 DB 自增改为应用赋值 10 位数字 (yyMMdd + 4 位序列, Redis 按日期计数) - 会议开始时间不能晚于结束时间校验; 参会人表单身份证附件/角色单独一行 - 会议详情左右列比例 7:3 -> 8:2 (材料审核列收窄) - admin/workbench 用户总数/角色类型数/各角色分布统计修复 (改用 listByRole 按 role_type 过滤) - 新增 admin 用户管理接口 + 门户页脚 + H5 签到/上传优化
This commit is contained in:
@@ -84,14 +84,6 @@ logging:
|
||||
org.springframework: debug
|
||||
com.ruoyi.business: debug
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
# 密码最大错误次数
|
||||
maxRetryCount: 5
|
||||
# 密码锁定时间(默认10分钟)
|
||||
lockTime: 10
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
# 资源信息
|
||||
@@ -145,8 +137,8 @@ token:
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
secret: abcdefghijklmnopqrstuvwxyz
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
# 令牌有效期(4小时)
|
||||
expireTime: 240
|
||||
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.ruoyi.business.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.business.domain.dto.AdminUserCreateBody;
|
||||
import com.ruoyi.business.service.IBizAdminUserService;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
|
||||
/**
|
||||
* admin/后台 新建用户 (按角色级联).
|
||||
* POST /business/adminUser/create — 仅后台管理员可调.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/business/adminUser")
|
||||
public class BizAdminUserController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IBizAdminUserService bizAdminUserService;
|
||||
|
||||
@PostMapping("/create")
|
||||
public AjaxResult create(@RequestBody AdminUserCreateBody body) {
|
||||
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||
if (!"admin".equals(roleType)) {
|
||||
throw new ServiceException("只有后台管理员可新建用户");
|
||||
}
|
||||
return success(bizAdminUserService.create(body));
|
||||
}
|
||||
}
|
||||
+4
-16
@@ -9,7 +9,6 @@ import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.business.domain.BizMeeting;
|
||||
@@ -252,11 +251,10 @@ public class BizMeetingAttendeeController extends BaseController {
|
||||
/**
|
||||
* 下载"劳务协议"空目录模板 zip (参会人管理 "下载协议模板" 按钮).
|
||||
* zip 内为 劳务协议/{序号}_{姓名}/ 空目录, 用户把签好的协议放进对应目录后重新压缩上传.
|
||||
* 仅 admin/manager 可触发.
|
||||
* 不限角色可触发.
|
||||
*/
|
||||
@GetMapping("/agreementTemplate/{meetingId}")
|
||||
public void agreementTemplate(@PathVariable("meetingId") Long meetingId, HttpServletResponse response) throws Exception {
|
||||
requireManagerOrAdmin();
|
||||
byte[] data = attendeeService.buildAgreementTemplateZip(meetingId);
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=agreement-template.zip");
|
||||
@@ -266,24 +264,22 @@ public class BizMeetingAttendeeController extends BaseController {
|
||||
|
||||
/**
|
||||
* 上传"劳务协议" zip, 解压后按 劳务协议/{序号}_{姓名}/ 目录匹配参会人,
|
||||
* 上传 OSS 并回填 labor_protocol. 仅 admin/manager 可触发.
|
||||
* 上传 OSS 并回填 labor_protocol. 不限角色可触发.
|
||||
*/
|
||||
@Log(title = "劳务协议回填", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/uploadAgreements")
|
||||
public AjaxResult uploadAgreements(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("meetingId") Long meetingId) throws Exception {
|
||||
requireManagerOrAdmin();
|
||||
int updated = attendeeService.uploadAgreements(file, meetingId);
|
||||
return success(updated);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载"专家照片"空目录模板 zip (专家照片 "下载目录模板" 按钮).
|
||||
* zip 内为 专家照片/{序号}_{姓名}/ 空目录. 仅 admin/manager 可触发.
|
||||
* zip 内为 专家照片/{序号}_{姓名}/ 空目录. 不限角色可触发.
|
||||
*/
|
||||
@GetMapping("/expertPhotoTemplate/{meetingId}")
|
||||
public void expertPhotoTemplate(@PathVariable("meetingId") Long meetingId, HttpServletResponse response) throws Exception {
|
||||
requireManagerOrAdmin();
|
||||
byte[] data = attendeeService.buildExpertPhotoTemplateZip(meetingId);
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=expert-photo-template.zip");
|
||||
@@ -293,22 +289,14 @@ public class BizMeetingAttendeeController extends BaseController {
|
||||
|
||||
/**
|
||||
* 上传"专家照片" zip, 解压后按 专家照片/{序号}_{姓名}/ 目录匹配参会人,
|
||||
* 上传 OSS 并逗号拼接回填 on_site_photos. 仅 admin/manager 可触发.
|
||||
* 上传 OSS 并逗号拼接回填 on_site_photos. 不限角色可触发.
|
||||
*/
|
||||
@Log(title = "专家照片回填", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/uploadExpertPhotos")
|
||||
public AjaxResult uploadExpertPhotos(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("meetingId") Long meetingId) throws Exception {
|
||||
requireManagerOrAdmin();
|
||||
int updated = attendeeService.uploadExpertPhotos(file, meetingId);
|
||||
return success(updated);
|
||||
}
|
||||
|
||||
/** 仅 admin/manager 可操作 (参会人劳务协议/专家照片的批量收发是管理端功能) */
|
||||
private void requireManagerOrAdmin() {
|
||||
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||
if (!"admin".equals(roleType) && !"manager".equals(roleType)) {
|
||||
throw new ServiceException("只有管理员或合规经理可操作");
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
-6
@@ -95,6 +95,10 @@ public class BizMeetingController extends BaseController {
|
||||
bizMeeting.getParams().put("executorUserId", uid);
|
||||
}
|
||||
}
|
||||
// 合规人员(manager) 数据权限: 只看"本人创建的项目"下的会议 (project_id ∈ create_user_id = 自己的项目)
|
||||
else if ("manager".equals(roleType)) {
|
||||
bizMeeting.getParams().put("managerCreateUserId", uid);
|
||||
}
|
||||
startPage();
|
||||
List<BizMeeting> list = bizMeetingService.selectList(bizMeeting);
|
||||
return getDataTable(list);
|
||||
@@ -110,7 +114,7 @@ public class BizMeetingController extends BaseController {
|
||||
public AjaxResult add(@RequestBody BizMeeting bizMeeting) {
|
||||
// executor 建会限额: 执行机构人员 (MAIN/SUB 只要能看到项目) 都可建会, 但该项目的会议数不得超过分配给本公司的场次.
|
||||
// 场次是公司维度: SUB 执行人反查主账号 parent_user_id 聚合 (与项目列表 assigned_sessions 口径一致).
|
||||
// 注意: 会议数按"该项目下全部未软删会议"计数 (biz_meeting 无执行方归属列, 无法区分是哪个执行方建的) — 见 memory [[ry-executor-staff-project-visibility]].
|
||||
// 会议数按"本执行方 (execution_unit_id)"计数, 不再按项目全量计数 — 多执行方分摊时各自独立.
|
||||
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||
if ("executor".equals(roleType)) {
|
||||
Long uid = SecurityUtils.getUserId();
|
||||
@@ -123,8 +127,23 @@ public class BizMeetingController extends BaseController {
|
||||
if (current != null && "SUB".equals(current.getAccountType()) && current.getParentUserId() != null) {
|
||||
aggUid = current.getParentUserId();
|
||||
}
|
||||
// 执行方归属 (单一可信源反查): 主账号 (或 SUB 执行人的主账号) → executor biz_org.org_id, 落 execution_unit_id
|
||||
Long executionUnitId = bizOrgService.selectOrgIdByUserId(aggUid);
|
||||
bizMeeting.setExecutionUnitId(executionUnitId);
|
||||
int assigned = bizProjectService.countAssignedSessions(projectId, aggUid);
|
||||
int existing = bizMeetingService.countByProjectId(projectId);
|
||||
// 期数校验: 期数不得超过分配给本公司的场次
|
||||
Long periodNo = bizMeeting.getPeriodNo();
|
||||
if (periodNo != null && periodNo > assigned) {
|
||||
throw new ServiceException("期数不能超过分配给本公司的场次 (共 " + assigned + " 场)");
|
||||
}
|
||||
// 相同期数冲突校验: 本机构已创建同项目、同期数的会议则报错
|
||||
if (periodNo != null) {
|
||||
int dup = bizMeetingService.countByProjectIdExecutionUnitPeriod(projectId, executionUnitId, periodNo);
|
||||
if (dup > 0) {
|
||||
throw new ServiceException("本机构已创建第 " + periodNo + " 期会议, 请勿重复");
|
||||
}
|
||||
}
|
||||
int existing = bizMeetingService.countByProjectIdAndExecutionUnit(projectId, executionUnitId);
|
||||
if (existing >= assigned) {
|
||||
throw new ServiceException("本项目分配给本公司的场次为 " + assigned + " 场, 已建 " + existing + " 场, 已达上限");
|
||||
}
|
||||
@@ -136,6 +155,17 @@ public class BizMeetingController extends BaseController {
|
||||
bizMeeting.setUpdateTime(new Date());
|
||||
// 提交截止时间: 建会时按 end_time + 项目 submit_deadline_days 天 落库 (项目未设天数则为 null → 永不冻结)
|
||||
bizMeeting.setSubmitDeadline(computeSubmitDeadline(bizMeeting.getProjectId(), bizMeeting.getEndTime()));
|
||||
// 总期数 = 项目设置的总期数 (biz_project.total_sessions); 期数(第几期)由前端 period_no 填, 不用 DB 默认 1
|
||||
// 项目形式从项目继承 (biz_meeting.project_form 此前从未写入, 导致会议列表"项目形式"列一直为空)
|
||||
if (bizMeeting.getProjectId() != null) {
|
||||
BizProject proj = bizProjectService.getById(bizMeeting.getProjectId());
|
||||
if (proj != null) {
|
||||
if (proj.getTotalSessions() != null) {
|
||||
bizMeeting.setTotalPeriods(proj.getTotalSessions());
|
||||
}
|
||||
bizMeeting.setProjectForm(proj.getProjectForm());
|
||||
}
|
||||
}
|
||||
int rows = bizMeetingService.insert(bizMeeting);
|
||||
Long[] attendeeUserIds = bizMeeting.getAttendeeUserIds();
|
||||
if (attendeeUserIds != null && attendeeUserIds.length > 0) {
|
||||
@@ -150,6 +180,13 @@ public class BizMeetingController extends BaseController {
|
||||
public AjaxResult edit(@RequestBody BizMeeting bizMeeting) {
|
||||
bizMeeting.setUpdateBy(SecurityUtils.getUsername());
|
||||
bizMeeting.setUpdateTime(new Date());
|
||||
// 修改会议时项目形式也从项目继承 (与 add 一致, 避免 project_form 残留为空)
|
||||
if (bizMeeting.getProjectId() != null) {
|
||||
BizProject proj = bizProjectService.getById(bizMeeting.getProjectId());
|
||||
if (proj != null) {
|
||||
bizMeeting.setProjectForm(proj.getProjectForm());
|
||||
}
|
||||
}
|
||||
int rows = bizMeetingService.updateByPrimaryKey(bizMeeting);
|
||||
Long[] attendeeUserIds = bizMeeting.getAttendeeUserIds();
|
||||
if (attendeeUserIds != null && attendeeUserIds.length > 0) {
|
||||
@@ -376,10 +413,6 @@ public class BizMeetingController extends BaseController {
|
||||
m.setSettleTime(new Date());
|
||||
m.setCurrentStage(stageDeriver.derivePhysicalStage(m));
|
||||
bizMeetingService.updateByPrimaryKey(m);
|
||||
// 结算成功 → 触发项目金额重算 (全量 SUM 已结算会议, 幂等)
|
||||
if (m.getProjectId() != null) {
|
||||
bizProjectService.recomputeSettledAmounts(m.getProjectId());
|
||||
}
|
||||
appendAuditLog(m, "SETTLE", "APPROVED", "会议结算");
|
||||
return success("SETTLED");
|
||||
}
|
||||
|
||||
+10
@@ -124,6 +124,16 @@ public class BizPersonController extends BaseController
|
||||
{
|
||||
return toAjax(bizPersonService.changeOrgAdmin(bizPerson.getPersonId()));
|
||||
}
|
||||
/**
|
||||
* 重置人员登录密码 (默认 123456)
|
||||
* body: { personId } — 按 personId 反查 sys_user, 密码重置为默认值
|
||||
*/
|
||||
@Log(title = "重置人员密码", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/resetPassword")
|
||||
public AjaxResult resetPassword(@RequestBody BizPerson bizPerson)
|
||||
{
|
||||
return toAjax(bizPersonService.resetPassword(bizPerson.getPersonId()));
|
||||
}
|
||||
@Log(title = "人员", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable String[] ids)
|
||||
|
||||
+25
@@ -97,6 +97,11 @@ public class BizProjectController extends BaseController
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BizProject bizProject)
|
||||
{
|
||||
// 合规人员(manager): 只看本人创建的项目 (create_user_id = 当前用户); admin 不限制
|
||||
String roleType = SecurityUtils.getLoginUser().getUser().getRoleType();
|
||||
if ("manager".equals(roleType)) {
|
||||
bizProject.getParams().put("createUserId", SecurityUtils.getUserId());
|
||||
}
|
||||
startPage();
|
||||
List<BizProject> list = bizProjectService.selectList(bizProject);
|
||||
return getDataTable(list);
|
||||
@@ -183,6 +188,26 @@ public class BizProjectController extends BaseController
|
||||
{
|
||||
return success(bizProjectService.getById(projectId));
|
||||
}
|
||||
|
||||
/**
|
||||
* executor 建会页"总场次"口径: 分配给本执行方 (公司) 的场次, 而非项目总场次.
|
||||
* GET /business/project/{projectId}/assignedSessions
|
||||
* 返回 { assignedSessions: N } — MAIN 主账号按自己聚合; SUB 执行人反查主账号 (parent_user_id) 聚合.
|
||||
*/
|
||||
@GetMapping("/{projectId}/assignedSessions")
|
||||
public AjaxResult assignedSessions(@PathVariable("projectId") Long projectId)
|
||||
{
|
||||
Long uid = SecurityUtils.getUserId();
|
||||
Long aggUid = uid;
|
||||
SysUser current = uid == null ? null : sysUserMapper.selectUserById(uid);
|
||||
if (current != null && "SUB".equals(current.getAccountType()) && current.getParentUserId() != null) {
|
||||
aggUid = current.getParentUserId();
|
||||
}
|
||||
int assigned = bizProjectService.countAssignedSessions(projectId, aggUid);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("assignedSessions", assigned);
|
||||
return success(data);
|
||||
}
|
||||
@Log(title = "项目", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody BizProject bizProject)
|
||||
|
||||
@@ -59,6 +59,8 @@ public class BizMeeting extends BaseEntity {
|
||||
private Date updateTime;
|
||||
/** 所属项目ID */
|
||||
private Long projectId;
|
||||
/** 执行方归属 (biz_org.org_id, org_type='executor') — 会议级费用/场次按执行方隔离的单一可信源 */
|
||||
private Long executionUnitId;
|
||||
/** 项目名称 */
|
||||
private String projectName;
|
||||
/** 所属公司名称 (派生字段, 由 biz_project.sponsor_org_id JOIN biz_org.org_name 得出, 不落库) */
|
||||
@@ -158,6 +160,8 @@ public class BizMeeting extends BaseEntity {
|
||||
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public Long getExecutionUnitId() { return executionUnitId; }
|
||||
public void setExecutionUnitId(Long executionUnitId) { this.executionUnitId = executionUnitId; }
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
public String getOrgName() { return orgName; }
|
||||
@@ -165,6 +169,10 @@ public class BizMeeting extends BaseEntity {
|
||||
private String address;
|
||||
public String getAddress() { return address; }
|
||||
public void setAddress(String address) { this.address = address; }
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public String getSupervisionOpinion() { return supervisionOpinion; }
|
||||
public void setSupervisionOpinion(String supervisionOpinion) { this.supervisionOpinion = supervisionOpinion; }
|
||||
public String getSupervisionBy() { return supervisionBy; }
|
||||
|
||||
@@ -129,8 +129,6 @@ public class BizProject extends BaseEntity {
|
||||
/** 发布时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date publishTime;
|
||||
/** 公告类型 (邀请函/支持函/通知/日程/公示) */
|
||||
private String announcementType;
|
||||
/** 开通截止时间 (date, 到期后由 OpenStatusScheduler 置 open_status=N) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date openDeadline;
|
||||
@@ -241,8 +239,6 @@ public class BizProject extends BaseEntity {
|
||||
public void setIsPublished(String isPublished) { this.isPublished = isPublished; }
|
||||
public Date getPublishTime() { return publishTime; }
|
||||
public void setPublishTime(Date publishTime) { this.publishTime = publishTime; }
|
||||
public String getAnnouncementType() { return announcementType; }
|
||||
public void setAnnouncementType(String announcementType) { this.announcementType = announcementType; }
|
||||
public Date getOpenDeadline() { return openDeadline; }
|
||||
public void setOpenDeadline(Date openDeadline) { this.openDeadline = openDeadline; }
|
||||
public String getOpenStatus() { return openStatus; }
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.ruoyi.business.domain.dto;
|
||||
|
||||
/**
|
||||
* admin/后台「新建用户」请求体 (按 roleType 级联, 各角色只填自己需要的字段).
|
||||
* <p>
|
||||
* roleType 决定级联分支:
|
||||
* <ul>
|
||||
* <li>admin / manager: 纯 sys_user (userName/nickName/phonenumber/email/password)</li>
|
||||
* <li>doctor: sys_user + biz_expert (workUnit/department/title/cert)</li>
|
||||
* <li>executor / sponsor MAIN: sys_user + biz_org + biz_person (orgName/businessNature/contact)</li>
|
||||
* <li>executor / sponsor SUB: sys_user + biz_person (orgId 选已有单位)</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class AdminUserCreateBody {
|
||||
/** 业务角色 admin/manager/doctor/executor/sponsor */
|
||||
private String roleType;
|
||||
/** 登录账号 (4-20 位字母/数字/下划线) */
|
||||
private String userName;
|
||||
/** 姓名 (doctor/person 必填; MAIN 账号默认用联系人/单位名) */
|
||||
private String nickName;
|
||||
/** 明文密码 (管理员手填, 后端加密) */
|
||||
private String password;
|
||||
/** 手机号 */
|
||||
private String phonenumber;
|
||||
/** 邮箱 */
|
||||
private String email;
|
||||
/** 状态 '0' 启用 / '1' 停用 */
|
||||
private String status;
|
||||
|
||||
// ===== doctor 专用 =====
|
||||
private String workUnit;
|
||||
private String department;
|
||||
private String title;
|
||||
private String practiceCertUrl;
|
||||
private String titleCertUrl;
|
||||
|
||||
// ===== executor / sponsor 专用 =====
|
||||
/** MAIN=新建单位(主账号) / SUB=选已有单位(子账号) */
|
||||
private String accountType;
|
||||
/** SUB: 所属单位 biz_org.org_id */
|
||||
private Long orgId;
|
||||
/** MAIN: 单位名称 */
|
||||
private String orgName;
|
||||
/** MAIN: 企业性质 */
|
||||
private String businessNature;
|
||||
/** MAIN: 联系人 */
|
||||
private String contactName;
|
||||
/** MAIN: 联系电话 */
|
||||
private String contactPhone;
|
||||
/** person 职务 (SUB 用) */
|
||||
private String position;
|
||||
|
||||
public String getRoleType() { return roleType; }
|
||||
public void setRoleType(String roleType) { this.roleType = roleType; }
|
||||
public String getUserName() { return userName; }
|
||||
public void setUserName(String userName) { this.userName = userName; }
|
||||
public String getNickName() { return nickName; }
|
||||
public void setNickName(String nickName) { this.nickName = nickName; }
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
public String getPhonenumber() { return phonenumber; }
|
||||
public void setPhonenumber(String phonenumber) { this.phonenumber = phonenumber; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getWorkUnit() { return workUnit; }
|
||||
public void setWorkUnit(String workUnit) { this.workUnit = workUnit; }
|
||||
public String getDepartment() { return department; }
|
||||
public void setDepartment(String department) { this.department = department; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getPracticeCertUrl() { return practiceCertUrl; }
|
||||
public void setPracticeCertUrl(String practiceCertUrl) { this.practiceCertUrl = practiceCertUrl; }
|
||||
public String getTitleCertUrl() { return titleCertUrl; }
|
||||
public void setTitleCertUrl(String titleCertUrl) { this.titleCertUrl = titleCertUrl; }
|
||||
public String getAccountType() { return accountType; }
|
||||
public void setAccountType(String accountType) { this.accountType = accountType; }
|
||||
public Long getOrgId() { return orgId; }
|
||||
public void setOrgId(Long orgId) { this.orgId = orgId; }
|
||||
public String getOrgName() { return orgName; }
|
||||
public void setOrgName(String orgName) { this.orgName = orgName; }
|
||||
public String getBusinessNature() { return businessNature; }
|
||||
public void setBusinessNature(String businessNature) { this.businessNature = businessNature; }
|
||||
public String getContactName() { return contactName; }
|
||||
public void setContactName(String contactName) { this.contactName = contactName; }
|
||||
public String getContactPhone() { return contactPhone; }
|
||||
public void setContactPhone(String contactPhone) { this.contactPhone = contactPhone; }
|
||||
public String getPosition() { return position; }
|
||||
public void setPosition(String position) { this.position = position; }
|
||||
}
|
||||
+14
-14
@@ -22,39 +22,39 @@ import com.ruoyi.common.annotation.Excel;
|
||||
public class BizMeetingAttendeeImportVo {
|
||||
|
||||
/** 医生 (原"姓名") */
|
||||
@Excel(name = "医生", sort = 1)
|
||||
@Excel(name = "医生##ysxm", sort = 1)
|
||||
private String name;
|
||||
|
||||
/** 联系电话 (原"手机号", 必填, 按此查/建 sys_user) */
|
||||
@Excel(name = "联系电话", sort = 2)
|
||||
@Excel(name = "联系电话##lxdh", sort = 2)
|
||||
private String phone;
|
||||
|
||||
/** 医院 (原"工作单位") */
|
||||
@Excel(name = "医院", sort = 3)
|
||||
@Excel(name = "医院##yy", sort = 3)
|
||||
private String workUnit;
|
||||
|
||||
/** 科室 */
|
||||
@Excel(name = "科室", sort = 4)
|
||||
@Excel(name = "科室##ks", sort = 4)
|
||||
private String department;
|
||||
|
||||
/** 职称 */
|
||||
@Excel(name = "职称", sort = 5)
|
||||
@Excel(name = "职称##zc", sort = 5)
|
||||
private String title;
|
||||
|
||||
/** 身份证号 */
|
||||
@Excel(name = "身份证号", sort = 6)
|
||||
@Excel(name = "身份证号##sfzh", sort = 6)
|
||||
private String idCard;
|
||||
|
||||
/** 银行 */
|
||||
@Excel(name = "银行", sort = 7)
|
||||
@Excel(name = "银行##yh", sort = 7)
|
||||
private String bankName;
|
||||
|
||||
/** 银行卡号码 */
|
||||
@Excel(name = "银行卡号码", sort = 8)
|
||||
@Excel(name = "银行卡号码##yhkh", sort = 8)
|
||||
private String bankCard;
|
||||
|
||||
/** 开户行 */
|
||||
@Excel(name = "开户行", sort = 9)
|
||||
@Excel(name = "开户行##khh", sort = 9)
|
||||
private String bankBranch;
|
||||
|
||||
/** 角色 (原"劳务形式": 授课/主持/评审...) */
|
||||
@@ -62,23 +62,23 @@ public class BizMeetingAttendeeImportVo {
|
||||
private String laborForm;
|
||||
|
||||
/** 应发金额 (decimal, 元) */
|
||||
@Excel(name = "应发金额", sort = 11)
|
||||
@Excel(name = "应发金额##yfje", sort = 11)
|
||||
private BigDecimal feePreTax;
|
||||
|
||||
/** 个税税金 (decimal, 元) */
|
||||
@Excel(name = "个税税金", sort = 12)
|
||||
@Excel(name = "个税税金##sj", sort = 12)
|
||||
private BigDecimal tax;
|
||||
|
||||
/** 增值税及附加成本 (decimal, 元) */
|
||||
@Excel(name = "增值税及附加成本", sort = 13)
|
||||
@Excel(name = "增值税及附加成本##zzsjfjcb", sort = 13)
|
||||
private BigDecimal vatAndSurcharge;
|
||||
|
||||
/** 实发金额 (decimal, 元) */
|
||||
@Excel(name = "实发金额", sort = 14)
|
||||
@Excel(name = "实发金额##sfje", sort = 14)
|
||||
private BigDecimal fee;
|
||||
|
||||
/** 摘要 */
|
||||
@Excel(name = "摘要", sort = 15)
|
||||
@Excel(name = "摘要##zy", sort = 15)
|
||||
private String summary;
|
||||
|
||||
/** 账户名称(持卡人姓名) */
|
||||
|
||||
@@ -21,6 +21,10 @@ public interface BizMeetingMapper
|
||||
List<Long> selectIdListByProjectId(Long projectId);
|
||||
/** 建会限额用: 统计某项目下未软删的会议数 (executor 建会不得超过分配的场次) */
|
||||
int countByProjectId(Long projectId);
|
||||
/** 建会限额用 (执行方隔离): 统计某项目下、某执行方 (biz_meeting.execution_unit_id) 未软删的会议数 */
|
||||
int countByProjectIdAndExecutionUnit(@Param("projectId") Long projectId, @Param("executionUnitId") Long executionUnitId);
|
||||
/** 建会校验用 (执行方隔离): 统计某项目下、某执行方、某期数的未软删会议数 (相同期数冲突检测) */
|
||||
int countByProjectIdExecutionUnitPeriod(@Param("projectId") Long projectId, @Param("executionUnitId") Long executionUnitId, @Param("periodNo") Long periodNo);
|
||||
/**
|
||||
* 自动流转: start_time 已过 且 material 未提交 (NOT_SUBMITTED) 且未执行的会议 → 置 is_executed=1 并转 RUNNING.
|
||||
* <p>由 MeetingStageScheduler 每分钟触发. 事实 + current_stage 缓存一起写.
|
||||
@@ -31,11 +35,6 @@ public interface BizMeetingMapper
|
||||
* <p>由 MeetingStageScheduler 每分钟触发.
|
||||
*/
|
||||
int markFrozen();
|
||||
/**
|
||||
* 自动流转: material/voucher 都 APPROVED 且 最晚审核时间已过 1 自然日 且 current_stage 仍为 SUPERVISION_APPROVED → AWAITING_SETTLEMENT.
|
||||
* <p>由 MeetingStageScheduler 每分钟触发 (待结算的 24h 慢路径).
|
||||
*/
|
||||
int markSettlementReady();
|
||||
/**
|
||||
* 费用汇总调度器用: 查 fee_calc_status=0 且未软删的会议 id 列表.
|
||||
*/
|
||||
|
||||
@@ -29,11 +29,6 @@ public interface BizProjectMapper
|
||||
/** 提交权限用: 判断 user 是否该项目的执行方 (MAIN biz_project_assign 或 SUB biz_project_executor_assign), >0 即命中 */
|
||||
int countExecutorOfProject(@org.apache.ibatis.annotations.Param("projectId") Long projectId,
|
||||
@org.apache.ibatis.annotations.Param("userId") Long userId);
|
||||
/**
|
||||
* 会议结算后重算项目金额: 按"所有已结算会议"全量 SUM 回写 paid_labor_amount / paid_meeting_amount,
|
||||
* 并重算 available_amount = total_amount - manage_fee - 已支付劳务 - 已支付会务 (幂等, 无累计副作用).
|
||||
*/
|
||||
int recomputeSettledAmounts(@org.apache.ibatis.annotations.Param("projectId") Long projectId);
|
||||
/** 删除公告: 将 invitation_url / support_letter_url / publish_url 置 NULL */
|
||||
int clearAnnouncement(@org.apache.ibatis.annotations.Param("projectId") Long projectId);
|
||||
/** 开通到期回收: 到期(open_deadline <= 今天)的 open_status='Y' 置回 'N' */
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.ruoyi.business.oss;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -59,18 +61,41 @@ public class OssZipService
|
||||
if (q >= 0) u = u.substring(0, q);
|
||||
|
||||
String httpsHost = "https://" + ossConfMeta.getBucket() + "." + ossConfMeta.getEndpoint() + "/";
|
||||
if (u.startsWith(httpsHost)) return u.substring(httpsHost.length());
|
||||
if (u.startsWith(httpsHost)) return decodeKey(u.substring(httpsHost.length()));
|
||||
String httpHost = "http://" + ossConfMeta.getBucket() + "." + ossConfMeta.getEndpoint() + "/";
|
||||
if (u.startsWith(httpHost)) return u.substring(httpHost.length());
|
||||
if (u.startsWith(httpHost)) return decodeKey(u.substring(httpHost.length()));
|
||||
|
||||
// 兜底: 取 "://" 之后第一个 "/" 之后的部分 (CNAME / 自定义域名)
|
||||
int i = u.indexOf("://");
|
||||
if (i >= 0)
|
||||
{
|
||||
int slash = u.indexOf('/', i + 3);
|
||||
if (slash >= 0) return u.substring(slash + 1);
|
||||
if (slash >= 0) return decodeKey(u.substring(slash + 1));
|
||||
}
|
||||
return decodeKey(u);
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原前端 encodeURIComponent 编码的 key.
|
||||
* <p>
|
||||
* 前端 uploadToOss() 用「明文中文」做 OSS key 上传, 但把 encodeURIComponent 后的 URL 存进 DB,
|
||||
* 所以 DB 里的 URL 是编码过的 (如 2.6M-%E6%B5%8B%E8%AF%95_xx.pdf),
|
||||
* 而 OSS 里真实 key 是明文 (2.6M-测试_xx.pdf). 不还原直接 copyObject 会 NoSuchKey.
|
||||
* <p>
|
||||
* 旧数据若已是明文 URL, decode 对其幂等 (中文非 %XX 序列, 不会被改变), 无副作用.
|
||||
*/
|
||||
private String decodeKey(String key)
|
||||
{
|
||||
if (key == null) return null;
|
||||
try
|
||||
{
|
||||
return URLDecoder.decode(key, StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// 非法 % 序列 / 已解码旧 key, 原样返回
|
||||
return key;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
/** OSS 服务端 copy (同 bucket 内), 用于把散落的材料文件复制到 staging 前缀, 不经过 Java 内存 */
|
||||
|
||||
-20
@@ -67,24 +67,4 @@ public class MeetingStageScheduler
|
||||
log.warn("[MeetingStageScheduler] 冻结异常 (跳过, 下分钟再试)", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每分钟: material+voucher 都 APPROVED 且最晚审核时间已过 1 自然日 → 待结算 (24h 慢路径).
|
||||
*/
|
||||
@Scheduled(fixedRate = 60_000, initialDelay = 30_000)
|
||||
public void markSettlementReady()
|
||||
{
|
||||
try
|
||||
{
|
||||
int affected = meetingMapper.markSettlementReady();
|
||||
if (affected > 0)
|
||||
{
|
||||
log.info("[MeetingStageScheduler] 自动转待结算: 本次更新 {} 行", affected);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("[MeetingStageScheduler] 转待结算异常 (跳过, 下分钟再试)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.business.service;
|
||||
|
||||
import com.ruoyi.business.domain.dto.AdminUserCreateBody;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
|
||||
/**
|
||||
* admin/后台 新建用户 Service (按 roleType 级联)
|
||||
*/
|
||||
public interface IBizAdminUserService {
|
||||
/**
|
||||
* 按 roleType 级联创建用户:
|
||||
* admin/manager → sys_user
|
||||
* doctor → sys_user + biz_expert
|
||||
* executor/sponsor MAIN → sys_user + biz_org + biz_person
|
||||
* executor/sponsor SUB → sys_user + biz_person
|
||||
*
|
||||
* @return 新建的 SysUser (含 userId)
|
||||
*/
|
||||
SysUser create(AdminUserCreateBody body);
|
||||
}
|
||||
@@ -24,6 +24,10 @@ public interface IBizMeetingService
|
||||
void softDeleteCascadeBatch(Long[] meetingIds);
|
||||
/** 建会限额用: 统计某项目下未软删的会议数 (executor 建会不得超过分配的场次) */
|
||||
int countByProjectId(Long projectId);
|
||||
/** 建会限额用 (执行方隔离): 统计某项目下、某执行方未软删的会议数 */
|
||||
int countByProjectIdAndExecutionUnit(Long projectId, Long executionUnitId);
|
||||
/** 建会校验用 (执行方隔离): 统计某项目下、某执行方、某期数的未软删会议数 (相同期数冲突检测) */
|
||||
int countByProjectIdExecutionUnitPeriod(Long projectId, Long executionUnitId, Long periodNo);
|
||||
/** 标记会议费用待重算 (人员/材料变化触发, 幂等; 由 FeeCalcScheduler 汇总回写) */
|
||||
void markFeeCalcPending(Long meetingId);
|
||||
}
|
||||
|
||||
@@ -28,4 +28,8 @@ public interface IBizPersonService
|
||||
* 原管理员由 MAIN 降为 SUB, 其余子账号 re-point 到新管理员, biz_org.user_id 同步指向新管理员
|
||||
*/
|
||||
int changeOrgAdmin(String personId);
|
||||
/**
|
||||
* 重置人员登录密码: 按 personId 反查 sys_user, 密码重置为默认 123456 (与新建人员默认密码一致)
|
||||
*/
|
||||
int resetPassword(String personId);
|
||||
}
|
||||
|
||||
@@ -40,12 +40,6 @@ public interface IBizProjectService
|
||||
*/
|
||||
boolean isExecutorOfProject(Long projectId, Long userId);
|
||||
|
||||
/**
|
||||
* 会议结算后重算项目金额: 按"所有已结算会议"全量 SUM 回写 paid_labor_amount / paid_meeting_amount,
|
||||
* 并重算 available_amount. 幂等 (每次全量重算), 无累计副作用.
|
||||
*/
|
||||
void recomputeSettledAmounts(Long projectId);
|
||||
|
||||
/** 删除公告: 将 invitation_url / support_letter_url / publish_url 置 NULL (未发布) */
|
||||
int clearAnnouncement(Long projectId);
|
||||
|
||||
|
||||
@@ -19,16 +19,6 @@ import com.ruoyi.business.domain.BizMeeting;
|
||||
@Component
|
||||
public class StageDeriver
|
||||
{
|
||||
private static final long H24 = 24L * 3600 * 1000;
|
||||
|
||||
/** 待结算: 材料审核通过 且 材料审核时间已超 24h (用户拍板口径) */
|
||||
private boolean settlementReady(BizMeeting m)
|
||||
{
|
||||
if (!"APPROVED".equals(m.getMaterialAuditStage())) return false;
|
||||
if (m.getMaterialAuditTime() == null) return false;
|
||||
return System.currentTimeMillis() - m.getMaterialAuditTime().getTime() >= H24;
|
||||
}
|
||||
|
||||
private static boolean t(Integer v)
|
||||
{
|
||||
return v != null && v == 1;
|
||||
@@ -49,7 +39,7 @@ public class StageDeriver
|
||||
if ("REJECTED".equals(material)) return "RECTIFYING";
|
||||
if ("APPROVED".equals(material))
|
||||
{
|
||||
return settlementReady(m) ? "AWAITING_SETTLEMENT" : "SUPERVISION_APPROVED";
|
||||
return "AWAITING_SETTLEMENT";
|
||||
}
|
||||
if ("SUBMITTED".equals(material))
|
||||
{
|
||||
@@ -81,10 +71,10 @@ public class StageDeriver
|
||||
return "executor".equals(role) ? "已退回" : "待整改";
|
||||
}
|
||||
|
||||
// 材料已支持方通过
|
||||
// 材料已支持方通过 → 待结算 (支持方审通过即待结算, 不再有 24h 慢路径)
|
||||
if ("APPROVED".equals(material))
|
||||
{
|
||||
return settlementReady(m) ? "待结算" : "审核通过";
|
||||
return "待结算";
|
||||
}
|
||||
|
||||
// 材料在审 (SUBMITTED)
|
||||
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
package com.ruoyi.business.service.impl;
|
||||
|
||||
import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ruoyi.business.domain.BizExpert;
|
||||
import com.ruoyi.business.domain.BizOrg;
|
||||
import com.ruoyi.business.domain.BizPerson;
|
||||
import com.ruoyi.business.domain.dto.AdminUserCreateBody;
|
||||
import com.ruoyi.business.mapper.BizExpertMapper;
|
||||
import com.ruoyi.business.mapper.BizPersonMapper;
|
||||
import com.ruoyi.business.service.IBizAdminUserService;
|
||||
import com.ruoyi.business.service.IBizOrgService;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.id.IdGenerator;
|
||||
import com.ruoyi.common.utils.id.SnowflakeId;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
* admin/后台 新建用户 (按 roleType 级联, 一个事务).
|
||||
* 角色单一可信源 = sys_user.role_type (biz_user_role_bind 已废).
|
||||
*/
|
||||
@Service
|
||||
public class BizAdminUserServiceImpl implements IBizAdminUserService {
|
||||
|
||||
private static final Set<String> ROLE_WHITELIST = Set.of("admin", "manager", "doctor", "executor", "sponsor");
|
||||
|
||||
@Autowired
|
||||
private ISysUserService sysUserService;
|
||||
@Autowired
|
||||
private IBizOrgService bizOrgService;
|
||||
@Autowired
|
||||
private BizPersonMapper bizPersonMapper;
|
||||
@Autowired
|
||||
private BizExpertMapper bizExpertMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SysUser create(AdminUserCreateBody b) {
|
||||
String role = b.getRoleType();
|
||||
if (role == null || !ROLE_WHITELIST.contains(role)) {
|
||||
throw new ServiceException("非法角色");
|
||||
}
|
||||
String userName = b.getUserName();
|
||||
if (userName == null || !userName.matches("^[A-Za-z0-9_]{4,20}$")) {
|
||||
throw new ServiceException("登录账号需 4-20 位字母/数字/下划线");
|
||||
}
|
||||
String password = b.getPassword();
|
||||
if (password == null || password.length() < 6 || password.length() > 20) {
|
||||
throw new ServiceException("密码长度 6-20 位");
|
||||
}
|
||||
|
||||
// 查重 (username / phone / email)
|
||||
SysUser dup = new SysUser();
|
||||
dup.setUserName(userName);
|
||||
if (!sysUserService.checkUserNameUnique(dup)) {
|
||||
throw new ServiceException("登录账号已存在");
|
||||
}
|
||||
if (b.getPhonenumber() != null && !b.getPhonenumber().isEmpty()) {
|
||||
SysUser p = new SysUser();
|
||||
p.setPhonenumber(b.getPhonenumber());
|
||||
if (!sysUserService.checkPhoneUnique(p)) {
|
||||
throw new ServiceException("手机号已存在");
|
||||
}
|
||||
}
|
||||
if (b.getEmail() != null && !b.getEmail().isEmpty()) {
|
||||
SysUser e = new SysUser();
|
||||
e.setEmail(b.getEmail());
|
||||
if (!sysUserService.checkEmailUnique(e)) {
|
||||
throw new ServiceException("邮箱已存在");
|
||||
}
|
||||
}
|
||||
|
||||
String encPwd = SecurityUtils.encryptPassword(password);
|
||||
|
||||
switch (role) {
|
||||
case "admin":
|
||||
case "manager":
|
||||
return insertPlain(b, role, encPwd);
|
||||
case "doctor":
|
||||
return insertDoctor(b, encPwd);
|
||||
case "executor":
|
||||
case "sponsor":
|
||||
if ("SUB".equals(b.getAccountType())) {
|
||||
return insertSub(b, role, encPwd);
|
||||
}
|
||||
return insertMain(b, role, encPwd);
|
||||
default:
|
||||
throw new ServiceException("非法角色");
|
||||
}
|
||||
}
|
||||
|
||||
/** 通用 sys_user 基础字段 */
|
||||
private SysUser baseUser(AdminUserCreateBody b, String role, String encPwd) {
|
||||
SysUser u = new SysUser();
|
||||
u.setUserName(b.getUserName());
|
||||
u.setNickName(b.getNickName());
|
||||
u.setPhonenumber(b.getPhonenumber());
|
||||
u.setEmail(b.getEmail());
|
||||
u.setPassword(encPwd);
|
||||
u.setRoleType(role);
|
||||
u.setStatus(b.getStatus() == null ? "0" : b.getStatus());
|
||||
u.setDelFlag("0");
|
||||
u.setCreateBy(SecurityUtils.getUsername());
|
||||
return u;
|
||||
}
|
||||
|
||||
/** admin / manager: 纯 sys_user */
|
||||
private SysUser insertPlain(AdminUserCreateBody b, String role, String encPwd) {
|
||||
SysUser u = baseUser(b, role, encPwd);
|
||||
sysUserService.insertUser(u);
|
||||
return u;
|
||||
}
|
||||
|
||||
/** doctor: sys_user + biz_expert (admin 直接建档, audit_status=通过) */
|
||||
private SysUser insertDoctor(AdminUserCreateBody b, String encPwd) {
|
||||
if (b.getNickName() == null || b.getNickName().isEmpty()) {
|
||||
throw new ServiceException("姓名不能为空");
|
||||
}
|
||||
SysUser u = baseUser(b, "doctor", encPwd);
|
||||
sysUserService.insertUser(u);
|
||||
|
||||
BizExpert ex = new BizExpert();
|
||||
ex.setExpertId(IdGenerator.generateId());
|
||||
ex.setUserId(u.getUserId());
|
||||
ex.setName(b.getNickName());
|
||||
ex.setPhone(b.getPhonenumber());
|
||||
ex.setWorkUnit(b.getWorkUnit());
|
||||
ex.setDepartment(b.getDepartment());
|
||||
ex.setTitle(b.getTitle());
|
||||
ex.setPracticeCertUrl(b.getPracticeCertUrl());
|
||||
ex.setTitleCertUrl(b.getTitleCertUrl());
|
||||
ex.setAuditStatus("2"); // 通过
|
||||
ex.setStatus("Y");
|
||||
ex.setCreateBy(SecurityUtils.getUsername());
|
||||
bizExpertMapper.insert(ex);
|
||||
return u;
|
||||
}
|
||||
|
||||
/** executor/sponsor MAIN: sys_user(MAIN) + biz_org + biz_person(自己=管理员) */
|
||||
private SysUser insertMain(AdminUserCreateBody b, String role, String encPwd) {
|
||||
String orgName = b.getOrgName();
|
||||
if (orgName == null || orgName.isEmpty()) {
|
||||
throw new ServiceException("单位名称不能为空");
|
||||
}
|
||||
String contactName = b.getContactName() != null ? b.getContactName() : orgName;
|
||||
String contactPhone = b.getContactPhone() != null ? b.getContactPhone() : b.getPhonenumber();
|
||||
|
||||
SysUser u = baseUser(b, role, encPwd);
|
||||
u.setAccountType("MAIN");
|
||||
u.setPhonenumber(contactPhone);
|
||||
if (u.getNickName() == null || u.getNickName().isEmpty()) {
|
||||
u.setNickName(contactName);
|
||||
}
|
||||
sysUserService.insertUser(u);
|
||||
|
||||
BizOrg org = new BizOrg();
|
||||
org.setUserId(u.getUserId());
|
||||
org.setOrgName(orgName);
|
||||
org.setOrgType(role);
|
||||
org.setBusinessNature(b.getBusinessNature());
|
||||
org.setContactName(contactName);
|
||||
org.setContactPhone(contactPhone);
|
||||
org.setStatus("0");
|
||||
org.setCreateBy(SecurityUtils.getUsername());
|
||||
bizOrgService.insert(org);
|
||||
|
||||
BizPerson self = new BizPerson();
|
||||
SnowflakeId.injectIfEmpty(self, "personId");
|
||||
self.setName(contactName);
|
||||
self.setPhone(contactPhone);
|
||||
self.setOrgId(org.getOrgId());
|
||||
self.setDepartment("管理部");
|
||||
self.setPosition("管理员");
|
||||
self.setUnitType(role);
|
||||
self.setUserId(u.getUserId());
|
||||
self.setCreateBy(SecurityUtils.getUsername());
|
||||
self.setUpdateBy(SecurityUtils.getUsername());
|
||||
bizPersonMapper.insert(self);
|
||||
return u;
|
||||
}
|
||||
|
||||
/** executor/sponsor SUB: sys_user(SUB, parent=主账号) + biz_person */
|
||||
private SysUser insertSub(AdminUserCreateBody b, String role, String encPwd) {
|
||||
if (b.getOrgId() == null) {
|
||||
throw new ServiceException("请选择所属单位");
|
||||
}
|
||||
BizOrg org = bizOrgService.getById(b.getOrgId());
|
||||
if (org == null) {
|
||||
throw new ServiceException("所属单位不存在");
|
||||
}
|
||||
if (!role.equals(org.getOrgType())) {
|
||||
throw new ServiceException("所选单位类型与角色不匹配");
|
||||
}
|
||||
if (b.getNickName() == null || b.getNickName().isEmpty()) {
|
||||
throw new ServiceException("姓名不能为空");
|
||||
}
|
||||
|
||||
SysUser u = baseUser(b, role, encPwd);
|
||||
u.setAccountType("SUB");
|
||||
u.setParentUserId(org.getUserId()); // 主账号 user_id, 可为 null (单位暂无主账号时留空待分配)
|
||||
sysUserService.insertUser(u);
|
||||
|
||||
BizPerson p = new BizPerson();
|
||||
SnowflakeId.injectIfEmpty(p, "personId");
|
||||
p.setName(b.getNickName());
|
||||
p.setPhone(b.getPhonenumber());
|
||||
p.setEmail(b.getEmail());
|
||||
p.setOrgId(b.getOrgId());
|
||||
p.setDepartment(b.getDepartment());
|
||||
p.setPosition(b.getPosition());
|
||||
p.setUnitType(role);
|
||||
p.setUserId(u.getUserId());
|
||||
p.setCreateBy(SecurityUtils.getUsername());
|
||||
p.setUpdateBy(SecurityUtils.getUsername());
|
||||
bizPersonMapper.insert(p);
|
||||
return u;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -564,11 +564,16 @@ public class BizMeetingAttendeeServiceImpl implements IBizMeetingAttendeeService
|
||||
return buildTemplateZip(meetingId, EXPERT_PHOTO_ZIP_ROOT);
|
||||
}
|
||||
|
||||
/** 生成空目录模板 zip: {rootDir}/{序号}_{姓名}/, 每个参会人一个空目录 */
|
||||
/** 生成空目录模板 zip: 根目录 README.txt + {rootDir}/{序号}_{姓名}/ 空目录 (每个参会人一个) */
|
||||
private byte[] buildTemplateZip(Long meetingId, String rootDir) {
|
||||
List<BizMeetingAttendee> list = selectByMeetingId(meetingId);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (ZipOutputStream zos = new ZipOutputStream(baos, StandardCharsets.UTF_8)) {
|
||||
// 根目录 README.txt: 保证 zip 里至少有一个真实文件, 否则纯空目录的 zip 部分工具无法解压
|
||||
zos.putNextEntry(new ZipEntry("README.txt"));
|
||||
zos.write("请把文件放入指定目录后,压缩成zip包并上传".getBytes(StandardCharsets.UTF_8));
|
||||
zos.closeEntry();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
BizMeetingAttendee a = list.get(i);
|
||||
// 空目录 entry (以 / 结尾), 序号 = 列表 1 起下标, 与前端表格 type="index" 一致
|
||||
|
||||
+35
-2
@@ -1,5 +1,7 @@
|
||||
package com.ruoyi.business.service.impl;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -13,6 +15,7 @@ import com.ruoyi.business.mapper.BizMeetingMaterialMapper;
|
||||
import com.ruoyi.business.mapper.BizMeetingAuditLogMapper;
|
||||
import com.ruoyi.business.service.IBizMeetingService;
|
||||
import com.ruoyi.common.enums.BizMeetingStageEnum;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.utils.id.IdGenerator;
|
||||
|
||||
@Service
|
||||
@@ -30,6 +33,11 @@ public class BizMeetingServiceImpl implements IBizMeetingService
|
||||
private BizMeetingMaterialMapper materialMapper;
|
||||
@Autowired
|
||||
private BizMeetingAuditLogMapper auditLogMapper;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
/** 会议ID (meeting_id) 每开始日期的序列号 Redis key 前缀: biz:meeting:id:{yyMMdd} */
|
||||
private static final String MEETING_ID_SEQ_KEY_PREFIX = "biz:meeting:id:";
|
||||
|
||||
@Override
|
||||
public BizMeeting getById(Long meetingId)
|
||||
@@ -39,8 +47,11 @@ public class BizMeetingServiceImpl implements IBizMeetingService
|
||||
{ return bizMeetingMapper.selectList(entity); }
|
||||
@Override
|
||||
public int insert(BizMeeting entity) {
|
||||
// meetingId 走 DB AUTO_INCREMENT (BizProject 同模式: SnowflakeId.injectIfEmpty 对 Long setter 会 NoSuchMethodException 被吞掉, 无副作用);
|
||||
// businessId DDL NOT NULL UNIQUE, 前端无字段, 兜底用雪花 ID 字符串 (跟 meetingId 同源, 保证唯一)
|
||||
// meetingId: 从 DB AUTO_INCREMENT 改为应用赋值 — 10 位数字会议ID = 开始日期(yyMMdd) + 4 位序列号(0001 起, 每开始日期 Redis 独立计数)
|
||||
if (entity.getMeetingId() == null) {
|
||||
entity.setMeetingId(generateMeetingId(entity.getStartTime()));
|
||||
}
|
||||
// businessId DDL NOT NULL UNIQUE, 前端无字段, 兜底用雪花 ID 字符串 (保持原逻辑不变)
|
||||
if (entity.getBusinessId() == null || entity.getBusinessId().isEmpty()) {
|
||||
entity.setBusinessId(String.valueOf(IdGenerator.generateId()));
|
||||
}
|
||||
@@ -55,6 +66,20 @@ public class BizMeetingServiceImpl implements IBizMeetingService
|
||||
}
|
||||
return bizMeetingMapper.insert(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 10 位数字会议ID = 开始日期 yyMMdd + 4 位序列号.
|
||||
* 序列号存 Redis (key = biz:meeting:id:{yyMMdd}), INCR 原子自增, 无需加锁;
|
||||
* 数据量小, 单日超过 9999 的场景不考虑 (String.format %04d 溢出会变 5 位, 仍唯一).
|
||||
*/
|
||||
private Long generateMeetingId(Date startTime) {
|
||||
Date d = (startTime != null) ? startTime : new Date();
|
||||
String yyMMdd = new SimpleDateFormat("yyMMdd").format(d);
|
||||
Long seq = redisCache.redisTemplate.opsForValue().increment(MEETING_ID_SEQ_KEY_PREFIX + yyMMdd);
|
||||
long seqVal = (seq == null) ? 1L : seq;
|
||||
return Long.parseLong(yyMMdd + String.format("%04d", seqVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByPrimaryKey(BizMeeting entity)
|
||||
{ return bizMeetingMapper.updateByPrimaryKey(entity); }
|
||||
@@ -94,6 +119,14 @@ public class BizMeetingServiceImpl implements IBizMeetingService
|
||||
public int countByProjectId(Long projectId)
|
||||
{ return bizMeetingMapper.countByProjectId(projectId); }
|
||||
|
||||
@Override
|
||||
public int countByProjectIdAndExecutionUnit(Long projectId, Long executionUnitId)
|
||||
{ return bizMeetingMapper.countByProjectIdAndExecutionUnit(projectId, executionUnitId); }
|
||||
|
||||
@Override
|
||||
public int countByProjectIdExecutionUnitPeriod(Long projectId, Long executionUnitId, Long periodNo)
|
||||
{ return bizMeetingMapper.countByProjectIdExecutionUnitPeriod(projectId, executionUnitId, periodNo); }
|
||||
|
||||
@Override
|
||||
public void markFeeCalcPending(Long meetingId) {
|
||||
if (meetingId != null) {
|
||||
|
||||
+13
@@ -141,6 +141,19 @@ public class BizPersonServiceImpl implements IBizPersonService
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置人员登录密码为默认 123456 (与新建人员默认密码一致)
|
||||
* 走 sys_user.resetUserPwd 顺带刷新 pwd_update_date, 不走 updateUser 的动态 set (语义更精准)
|
||||
*/
|
||||
@Override
|
||||
public int resetPassword(String personId) {
|
||||
BizPerson p = bizPersonMapper.selectByPrimaryKey(personId);
|
||||
if (p == null || p.getUserId() == null) {
|
||||
throw new ServiceException("人员不存在或未关联登录账号");
|
||||
}
|
||||
return sysUserMapper.resetUserPwd(p.getUserId(), SecurityUtils.encryptPassword("123456"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteByPrimaryKeys(String[] personIds)
|
||||
{
|
||||
|
||||
-7
@@ -132,13 +132,6 @@ public class BizProjectServiceImpl implements IBizProjectService
|
||||
public boolean isExecutorOfProject(Long projectId, Long userId)
|
||||
{ return bizProjectMapper.countExecutorOfProject(projectId, userId) > 0; }
|
||||
|
||||
/** 会议结算后重算项目金额 (全量 SUM 已结算会议, 幂等) */
|
||||
@Override
|
||||
public void recomputeSettledAmounts(Long projectId) {
|
||||
if (projectId == null) return;
|
||||
bizProjectMapper.recomputeSettledAmounts(projectId);
|
||||
}
|
||||
|
||||
/** 删除公告: 将 3 个公示 URL 置 NULL (未发布) */
|
||||
@Override
|
||||
public int clearAnnouncement(Long projectId) {
|
||||
|
||||
@@ -41,10 +41,13 @@
|
||||
<select id="selectList" resultMap="BizExpertResult" parameterType="BizExpert">
|
||||
<include refid="selectFields"/>
|
||||
<where>
|
||||
<if test="name != null and name != ''"> and name = #{name}</if>
|
||||
<if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if>
|
||||
<if test="phone != null and phone != ''"> and phone = #{phone}</if>
|
||||
<if test="workUnit != null and workUnit != ''"> and work_unit like concat('%', #{workUnit}, '%')</if>
|
||||
<if test="department != null and department != ''"> and department = #{department}</if>
|
||||
<if test="title != null and title != ''"> and title = #{title}</if>
|
||||
<if test="auditStatus != null and auditStatus != ''"> and audit_status = #{auditStatus}</if>
|
||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
||||
</where>
|
||||
order by expert_id desc
|
||||
</select>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<id property="meetingId" column="meeting_id" />
|
||||
<result property="businessId" column="business_id" />
|
||||
<result property="projectId" column="project_id" />
|
||||
<result property="executionUnitId" column="execution_unit_id" />
|
||||
<result property="projectNo" column="project_no" />
|
||||
<result property="projectName" column="project_name" />
|
||||
<result property="meetingName" column="meeting_name" />
|
||||
@@ -15,6 +16,7 @@
|
||||
<result property="endTime" column="end_time" />
|
||||
<result property="orgName" column="org_name" />
|
||||
<result property="address" column="address" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="currentStage" column="current_stage" />
|
||||
<result property="supervisionOpinion" column="supervision_opinion" />
|
||||
<result property="supervisionBy" column="supervision_by" />
|
||||
@@ -49,7 +51,7 @@
|
||||
<result property="isDeleted" column="is_deleted" />
|
||||
</resultMap>
|
||||
<sql id="selectFields">
|
||||
meeting_id, business_id, project_id, project_no, project_name, meeting_name, period_no, total_periods, project_form, start_time, end_time, address, current_stage, supervision_opinion, supervision_by, supervision_time, material_audit_stage, is_executed, execute_time, is_settled, settle_time, is_finished, finish_time, is_frozen, freeze_time, material_audit_time, material_compliance_approved, submit_deadline, invitation_url, schedule_url, poster_url, labor_signed, labor_fee, meeting_fee, total_fee, fee_calc_status, create_by, create_time, update_by, update_time, is_deleted
|
||||
meeting_id, business_id, project_id, execution_unit_id, project_no, project_name, meeting_name, period_no, total_periods, project_form, start_time, end_time, address, remark, current_stage, supervision_opinion, supervision_by, supervision_time, material_audit_stage, is_executed, execute_time, is_settled, settle_time, is_finished, finish_time, is_frozen, freeze_time, material_audit_time, material_compliance_approved, submit_deadline, invitation_url, schedule_url, poster_url, labor_signed, labor_fee, meeting_fee, total_fee, fee_calc_status, create_by, create_time, update_by, update_time, is_deleted
|
||||
</sql>
|
||||
<select id="selectByPrimaryKey" resultMap="BizMeetingResult" parameterType="Long">
|
||||
select
|
||||
@@ -101,6 +103,10 @@
|
||||
select distinct a.project_id from biz_project_executor_assign a
|
||||
where a.is_deleted = 0 and a.staff_user_id = #{params.executorStaffUserId}
|
||||
)</if>
|
||||
<!-- 合规人员(manager) 数据权限: 只看本人创建项目的会议 -->
|
||||
<if test="params.managerCreateUserId != null">and project_id in (
|
||||
select project_id from biz_project where create_user_id = #{params.managerCreateUserId} and is_deleted = 0
|
||||
)</if>
|
||||
</where>
|
||||
order by meeting_id desc
|
||||
</select>
|
||||
@@ -110,6 +116,7 @@
|
||||
<if test="meetingId != null">meeting_id,</if>
|
||||
<if test="businessId != null and businessId != ''">business_id,</if>
|
||||
<if test="projectId != null">project_id,</if>
|
||||
<if test="executionUnitId != null">execution_unit_id,</if>
|
||||
<if test="projectNo != null and projectNo != ''">project_no,</if>
|
||||
<if test="projectName != null and projectName != ''">project_name,</if>
|
||||
<if test="meetingName != null and meetingName != ''">meeting_name,</if>
|
||||
@@ -119,6 +126,7 @@
|
||||
<if test="startTime != null">start_time,</if>
|
||||
<if test="endTime != null">end_time,</if>
|
||||
<if test="address != null and address != ''">address,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="currentStage != null and currentStage != ''">current_stage,</if>
|
||||
<if test="supervisionOpinion != null and supervisionOpinion != ''">supervision_opinion,</if>
|
||||
<if test="supervisionBy != null and supervisionBy != ''">supervision_by,</if>
|
||||
@@ -138,6 +146,7 @@
|
||||
<if test="meetingId != null">#{meetingId},</if>
|
||||
<if test="businessId != null and businessId != ''">#{businessId},</if>
|
||||
<if test="projectId != null">#{projectId},</if>
|
||||
<if test="executionUnitId != null">#{executionUnitId},</if>
|
||||
<if test="projectNo != null and projectNo != ''">#{projectNo},</if>
|
||||
<if test="projectName != null and projectName != ''">#{projectName},</if>
|
||||
<if test="meetingName != null and meetingName != ''">#{meetingName},</if>
|
||||
@@ -147,6 +156,7 @@
|
||||
<if test="startTime != null">#{startTime},</if>
|
||||
<if test="endTime != null">#{endTime},</if>
|
||||
<if test="address != null and address != ''">#{address},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="currentStage != null and currentStage != ''">#{currentStage},</if>
|
||||
<if test="supervisionOpinion != null and supervisionOpinion != ''">#{supervisionOpinion},</if>
|
||||
<if test="supervisionBy != null and supervisionBy != ''">#{supervisionBy},</if>
|
||||
@@ -168,6 +178,7 @@
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="businessId != null and businessId != ''">business_id = #{businessId},</if>
|
||||
<if test="projectId != null">project_id = #{projectId},</if>
|
||||
<if test="executionUnitId != null">execution_unit_id = #{executionUnitId},</if>
|
||||
<if test="projectNo != null and projectNo != ''">project_no = #{projectNo},</if>
|
||||
<if test="projectName != null and projectName != ''">project_name = #{projectName},</if>
|
||||
<if test="meetingName != null and meetingName != ''">meeting_name = #{meetingName},</if>
|
||||
@@ -178,6 +189,7 @@
|
||||
<if test="startTime != null">start_time = #{startTime},</if>
|
||||
<if test="endTime != null">end_time = #{endTime},</if>
|
||||
<if test="address != null and address != ''">address = #{address},</if>
|
||||
<if test="remark != null and remark != ''">remark = #{remark},</if>
|
||||
<if test="currentStage != null and currentStage != ''">current_stage = #{currentStage},</if>
|
||||
<if test="supervisionOpinion != null and supervisionOpinion != ''">supervision_opinion = #{supervisionOpinion},</if>
|
||||
<if test="supervisionBy != null and supervisionBy != ''">supervision_by = #{supervisionBy},</if>
|
||||
@@ -224,6 +236,17 @@
|
||||
<select id="countByProjectId" resultType="int" parameterType="Long">
|
||||
select count(*) from biz_meeting where project_id = #{projectId} and is_deleted = 0
|
||||
</select>
|
||||
<!-- 建会限额用 (执行方隔离): 某项目下、某执行方未软删的会议数 -->
|
||||
<select id="countByProjectIdAndExecutionUnit" resultType="int">
|
||||
select count(*) from biz_meeting where project_id = #{projectId} and is_deleted = 0
|
||||
and execution_unit_id = #{executionUnitId}
|
||||
</select>
|
||||
<!-- 建会校验用 (执行方隔离): 某项目下、某执行方、某期数的未软删会议数 (相同期数冲突检测) -->
|
||||
<select id="countByProjectIdExecutionUnitPeriod" resultType="int">
|
||||
select count(*) from biz_meeting where project_id = #{projectId} and is_deleted = 0
|
||||
and execution_unit_id = #{executionUnitId}
|
||||
and period_no = #{periodNo}
|
||||
</select>
|
||||
<!-- 自动流转 (MeetingStageScheduler 每分钟调): start_time 已过 且 material 未提交 且未执行的会议 → is_executed=1 + RUNNING. 已软删/已冻结/已提交材料的不动. -->
|
||||
<update id="markExecuted">
|
||||
update biz_meeting
|
||||
@@ -250,19 +273,6 @@
|
||||
and submit_deadline is not null
|
||||
and submit_deadline <= NOW()
|
||||
</update>
|
||||
<!-- 自动流转 (MeetingStageScheduler 每分钟调): 材料 APPROVED 且 材料审核时间已过 1 自然日 → AWAITING_SETTLEMENT (待结算 24h 慢路径) -->
|
||||
<update id="markSettlementReady">
|
||||
update biz_meeting
|
||||
set current_stage = 'AWAITING_SETTLEMENT'
|
||||
where is_deleted = 0
|
||||
and is_frozen = 0
|
||||
and is_settled = 0
|
||||
and is_finished = 0
|
||||
and material_audit_stage = 'APPROVED'
|
||||
and current_stage = 'SUPERVISION_APPROVED'
|
||||
and material_audit_time is not null
|
||||
and material_audit_time <= (NOW() - INTERVAL 1 DAY)
|
||||
</update>
|
||||
<!-- 费用汇总调度器: 查 fee_calc_status=0 且未软删的会议 id -->
|
||||
<select id="selectPendingFeeCalcIds" resultType="Long">
|
||||
select meeting_id from biz_meeting where fee_calc_status = 0 and is_deleted = 0
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<result property="projectForm" column="project_form" />
|
||||
<result property="isFinished" column="is_finished" />
|
||||
<result property="isSettled" column="is_settled" />
|
||||
<result property="isPublished" column="is_published" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createUserId" column="create_user_id" />
|
||||
<result property="createUserName" column="create_user_name" />
|
||||
@@ -49,7 +50,13 @@
|
||||
<result property="isDeleted" column="is_deleted" />
|
||||
</resultMap>
|
||||
<sql id="selectFields">
|
||||
select p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions, p.total_amount, p.available_amount, p.paid_labor_amount, p.paid_meeting_amount, p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, 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,
|
||||
select p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions, p.total_amount,
|
||||
(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) as paid_labor_amount,
|
||||
(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 paid_meeting_amount,
|
||||
(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,
|
||||
o.org_name as sponsor_org_name,
|
||||
su.user_name as sponsor_admin_user_name,
|
||||
lu.user_name as lead_user_name,
|
||||
@@ -68,8 +75,13 @@
|
||||
<!-- sponsor 端专属查询: 与 selectFields 等价 (sponsor 评分改走 biz_project_rating 子表, 这里只查项目本体) -->
|
||||
<sql id="selectFieldsForSponsor">
|
||||
select p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions,
|
||||
p.total_amount, p.available_amount, p.paid_labor_amount, p.paid_meeting_amount,
|
||||
p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled,
|
||||
p.total_amount,
|
||||
(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) as paid_labor_amount,
|
||||
(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 paid_meeting_amount,
|
||||
(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,
|
||||
@@ -147,9 +159,11 @@
|
||||
1) a.exec_user_id = 当前 user_id (子账号直接被指派)
|
||||
2) a.execution_unit_id = 当前 user_id 对应的 executor biz_org.org_id (主账号整公司被指派)
|
||||
注: executor 角色无评分/聚合分需求, 复用 selectFields (含 sponsor_score / manager_score)
|
||||
金额列口径 (执行方级): 总金额=assigned_amount(本执行方被分配), 已支付劳务/会务/可用金额 均按
|
||||
biz_meeting.execution_unit_id = 本执行方 org 过滤, 与项目级 total_amount/available_amount 无关.
|
||||
-->
|
||||
<select id="selectExecutorList" resultMap="BizProjectResult" parameterType="BizProject">
|
||||
select distinct p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions, p.total_amount, p.available_amount, p.paid_labor_amount, p.paid_meeting_amount, p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, 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,
|
||||
select distinct p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions, p.total_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,
|
||||
o.org_name as sponsor_org_name,
|
||||
su.user_name as sponsor_admin_user_name,
|
||||
lu.user_name as lead_user_name,
|
||||
@@ -168,7 +182,37 @@
|
||||
where bpa4.project_id = p.project_id
|
||||
and bpa4.is_deleted = 0
|
||||
and bpa4.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as assigned_amount,
|
||||
(select count(*) from biz_meeting m where m.project_id = p.project_id and m.is_deleted = 0) as meeting_count
|
||||
(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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as paid_labor_amount,
|
||||
(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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as paid_meeting_amount,
|
||||
(select coalesce(sum(bpa5.amount), 0)
|
||||
from biz_project_assign bpa5
|
||||
where bpa5.project_id = p.project_id
|
||||
and bpa5.is_deleted = 0
|
||||
and bpa5.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor'))
|
||||
- (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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor'))
|
||||
- (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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as available_amount,
|
||||
(select count(*) from biz_meeting m where m.project_id = p.project_id and m.is_deleted = 0
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as meeting_count
|
||||
from biz_project p
|
||||
<!-- 执行方专属 join: 把 executor 限定条件放进 ON (而不是 WHERE), 这样 join 只命中分给当前执行方的 assignment, 1 行/项目. SELECT DISTINCT 保留以防 LEFT JOIN 副作用 -->
|
||||
join biz_project_assign a on a.project_id = p.project_id
|
||||
@@ -194,9 +238,10 @@
|
||||
(主账号把执行人派到项目上后, 执行人登录看自己被派到的项目)
|
||||
严格隔离: 不 JOIN biz_project_assign (避免主账号过滤), 不 UNION intent (executor 与 biz_*_intent 完全无关)
|
||||
场次/金额列 assigned_sessions/assigned_amount 按 params.executorUserId (主账号/本公司) 聚合 — 执行人看到的是公司数据, 不是个人数据
|
||||
已支付劳务/会务/可用金额同样按 biz_meeting.execution_unit_id = 本公司 org 过滤 (执行方级).
|
||||
-->
|
||||
<select id="selectExecutorStaffList" resultMap="BizProjectResult" parameterType="BizProject">
|
||||
select p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions, p.total_amount, p.available_amount, p.paid_labor_amount, p.paid_meeting_amount, p.manager_score, p.sponsor_score, p.project_form, p.is_finished, p.is_settled, 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,
|
||||
select p.project_id, p.project_no, p.project_name, p.total_sessions, p.done_sessions, p.todo_sessions, p.total_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,
|
||||
o.org_name as sponsor_org_name,
|
||||
su.user_name as sponsor_admin_user_name,
|
||||
lu.user_name as lead_user_name,
|
||||
@@ -215,7 +260,37 @@
|
||||
where bpa4.project_id = p.project_id
|
||||
and bpa4.is_deleted = 0
|
||||
and bpa4.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as assigned_amount,
|
||||
(select count(*) from biz_meeting m where m.project_id = p.project_id and m.is_deleted = 0) as meeting_count
|
||||
(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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as paid_labor_amount,
|
||||
(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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as paid_meeting_amount,
|
||||
(select coalesce(sum(bpa5.amount), 0)
|
||||
from biz_project_assign bpa5
|
||||
where bpa5.project_id = p.project_id
|
||||
and bpa5.is_deleted = 0
|
||||
and bpa5.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor'))
|
||||
- (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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor'))
|
||||
- (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
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as available_amount,
|
||||
(select count(*) from biz_meeting m where m.project_id = p.project_id and m.is_deleted = 0
|
||||
and m.execution_unit_id = (select org_id from biz_org where user_id = #{params.executorUserId} and org_type = 'executor')) as meeting_count
|
||||
from biz_project p
|
||||
left join biz_org o on o.org_id = p.sponsor_org_id and o.org_type = 'sponsor'
|
||||
left join sys_user su on su.user_id = o.user_id
|
||||
@@ -241,6 +316,8 @@
|
||||
<include refid="selectFields"/>
|
||||
<where>
|
||||
p.is_deleted = 0
|
||||
<if test="params.createUserId != null">and create_user_id = #{params.createUserId}</if>
|
||||
<if test="isPublished != null and isPublished != ''">and p.is_published = #{isPublished}</if>
|
||||
<if test="params.projectIds != null and params.projectIds.size() > 0">
|
||||
and project_id in
|
||||
<foreach collection="params.projectIds" item="id" open="(" separator="," close=")">
|
||||
@@ -289,14 +366,12 @@
|
||||
<if test="doneSessions != null">done_sessions,</if>
|
||||
<if test="todoSessions != null">todo_sessions,</if>
|
||||
<if test="totalAmount != null">total_amount,</if>
|
||||
<if test="availableAmount != null">available_amount,</if>
|
||||
<if test="paidLaborAmount != null">paid_labor_amount,</if>
|
||||
<if test="paidMeetingAmount != null">paid_meeting_amount,</if>
|
||||
<if test="managerScore != null">manager_score,</if>
|
||||
<if test="sponsorScore != null">sponsor_score,</if>
|
||||
<if test="projectForm != null and projectForm != ''">project_form,</if>
|
||||
<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="sponsorOrgId != null">sponsor_org_id,</if>
|
||||
<if test="leadUserId != null">lead_user_id,</if>
|
||||
<if test="isBidProject != null and isBidProject != ''">is_bid_project,</if>
|
||||
@@ -335,6 +410,7 @@
|
||||
<if test="projectForm != null and projectForm != ''">#{projectForm},</if>
|
||||
<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="sponsorOrgId != null">#{sponsorOrgId},</if>
|
||||
<if test="leadUserId != null">#{leadUserId},</if>
|
||||
<if test="isBidProject != null and isBidProject != ''">#{isBidProject},</if>
|
||||
@@ -380,14 +456,12 @@
|
||||
<if test="doneSessions != null">done_sessions = #{doneSessions},</if>
|
||||
<if test="todoSessions != null">todo_sessions = #{todoSessions},</if>
|
||||
<if test="totalAmount != null">total_amount = #{totalAmount},</if>
|
||||
<if test="availableAmount != null">available_amount = #{availableAmount},</if>
|
||||
<if test="paidLaborAmount != null">paid_labor_amount = #{paidLaborAmount},</if>
|
||||
<if test="paidMeetingAmount != null">paid_meeting_amount = #{paidMeetingAmount},</if>
|
||||
<if test="managerScore != null">manager_score = #{managerScore},</if>
|
||||
<if test="sponsorScore != null">sponsor_score = #{sponsorScore},</if>
|
||||
<if test="projectForm != null and projectForm != ''">project_form = #{projectForm},</if>
|
||||
<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="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>
|
||||
@@ -399,13 +473,14 @@
|
||||
</trim>
|
||||
where project_id = #{projectId}
|
||||
</update>
|
||||
<!-- 删除公告: 将 4 个公示 URL 字段全部置 NULL (未发布态由 URL 是否为空判定, 表里无独立 is_published 列) -->
|
||||
<!-- 删除公告: 将 4 个公示 URL 字段置 NULL 并 is_published='0' (取消发布) -->
|
||||
<update id="clearAnnouncement" parameterType="Long">
|
||||
update biz_project
|
||||
set invitation_url = null,
|
||||
support_letter_url = null,
|
||||
publish_url = null,
|
||||
schedule_url = null
|
||||
schedule_url = null,
|
||||
is_published = '0'
|
||||
where project_id = #{projectId}
|
||||
</update>
|
||||
<!-- 开通到期回收: 每天 00:05 由 OpenStatusScheduler 调用, 到期(open_deadline <= 今天)的 Y 置回 N -->
|
||||
@@ -461,28 +536,4 @@
|
||||
and b.staff_user_id = #{userId}
|
||||
) t
|
||||
</select>
|
||||
<!-- 会议结算后重算项目金额: 全量 SUM 已结算会议 (is_settled=1) 的 labor_fee/meeting_fee,
|
||||
幂等回写 paid_labor_amount / paid_meeting_amount, 并重算 available_amount = 总金额 - 管理费 - 已支付劳务 - 已支付会务.
|
||||
单条 UPDATE 原子执行, 多会议并发结算同一项目时无丢失更新 (每次都是全量重算). -->
|
||||
<update id="recomputeSettledAmounts" parameterType="Long">
|
||||
update biz_project p
|
||||
set p.paid_labor_amount = (
|
||||
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
|
||||
),
|
||||
p.paid_meeting_amount = (
|
||||
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
|
||||
),
|
||||
p.available_amount = 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)
|
||||
where p.project_id = #{projectId}
|
||||
</update>
|
||||
</mapper>
|
||||
+1
-2
@@ -11,7 +11,6 @@ import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 认证失败处理类 返回未授权
|
||||
@@ -28,7 +27,7 @@ public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, S
|
||||
throws IOException
|
||||
{
|
||||
int code = HttpStatus.UNAUTHORIZED;
|
||||
String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
|
||||
String msg = "认证失败,请重新登陆";
|
||||
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-28
@@ -1,15 +1,12 @@
|
||||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
|
||||
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
|
||||
|
||||
@@ -24,12 +21,6 @@ public class SysPasswordService
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Value(value = "${user.password.maxRetryCount}")
|
||||
private int maxRetryCount;
|
||||
|
||||
@Value(value = "${user.password.lockTime}")
|
||||
private int lockTime;
|
||||
|
||||
/**
|
||||
* 登录账户密码错误次数缓存键名
|
||||
*
|
||||
@@ -44,31 +35,13 @@ public class SysPasswordService
|
||||
public void validate(SysUser user)
|
||||
{
|
||||
Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
|
||||
String username = usernamePasswordAuthenticationToken.getName();
|
||||
String password = usernamePasswordAuthenticationToken.getCredentials().toString();
|
||||
|
||||
Integer retryCount = redisCache.getCacheObject(getCacheKey(username));
|
||||
|
||||
if (retryCount == null)
|
||||
{
|
||||
retryCount = 0;
|
||||
}
|
||||
|
||||
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
|
||||
{
|
||||
throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
|
||||
}
|
||||
|
||||
// 去掉多次登录失败锁定: 只校验密码, 错误直接抛, 不再计数/锁定
|
||||
if (!matches(user, password))
|
||||
{
|
||||
retryCount = retryCount + 1;
|
||||
redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
else
|
||||
{
|
||||
clearLoginRecordCache(username);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(SysUser user, String rawPassword)
|
||||
|
||||
@@ -16,6 +16,9 @@ public class SysUserExtendVo extends SysUser
|
||||
/** 业务角色 (admin / manager / doctor / executor / sponsor) */
|
||||
private String roleType;
|
||||
|
||||
/** 单位名称 (MAIN 走 biz_org.user_id, SUB 走 biz_person.org_id 反查, 仅 list 展示) */
|
||||
private String orgName;
|
||||
|
||||
public String getRoleType()
|
||||
{
|
||||
return roleType;
|
||||
@@ -25,4 +28,14 @@ public class SysUserExtendVo extends SysUser
|
||||
{
|
||||
this.roleType = roleType;
|
||||
}
|
||||
|
||||
public String getOrgName()
|
||||
{
|
||||
return orgName;
|
||||
}
|
||||
|
||||
public void setOrgName(String orgName)
|
||||
{
|
||||
this.orgName = orgName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<!-- 用户扩展视图 resultMap: 继承 SysUserResult, 附加 sys_user.role_type 业务角色 -->
|
||||
<resultMap type="SysUserExtendVo" id="SysUserExtendResult" extends="SysUserResult">
|
||||
<result property="roleType" column="u_role_type" />
|
||||
<result property="orgName" column="org_name" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectUserVo">
|
||||
@@ -99,7 +100,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
-->
|
||||
<select id="selectUserExtendList" parameterType="SysUserExtendVo" resultMap="SysUserExtendResult">
|
||||
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.update_by, u.update_time, u.remark, d.dept_name, d.leader,
|
||||
u.role_type as u_role_type
|
||||
u.role_type as u_role_type,
|
||||
coalesce(
|
||||
(select o.org_name from biz_org o where o.user_id = u.user_id limit 1),
|
||||
(select o.org_name from biz_org o join biz_person p on p.org_id = o.org_id where p.user_id = u.user_id limit 1)
|
||||
) as org_name
|
||||
from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
where u.del_flag = '0'
|
||||
@@ -124,6 +129,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="deptId != null and deptId != 0">
|
||||
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
|
||||
</if>
|
||||
<if test="nickName != null and nickName != ''"><!-- 姓名过滤 -->
|
||||
AND u.nick_name like concat('%', #{nickName}, '%')
|
||||
</if>
|
||||
<if test="orgName != null and orgName != ''"><!-- 单位名称过滤 (MAIN 走 biz_org, SUB 走 biz_person) -->
|
||||
AND (
|
||||
exists (select 1 from biz_org o where o.user_id = u.user_id and o.org_name like concat('%', #{orgName}, '%'))
|
||||
or exists (select 1 from biz_person p join biz_org o2 on o2.org_id = p.org_id where p.user_id = u.user_id and o2.org_name like concat('%', #{orgName}, '%'))
|
||||
)
|
||||
</if>
|
||||
<if test="roleType != null and roleType != ''"><!-- 业务角色过滤 -->
|
||||
AND u.role_type = #{roleType}
|
||||
</if>
|
||||
|
||||
Reference in New Issue
Block a user