feat(项目管理): 招标字段/负责人/导出/下拉/登录短信 完整链路

后端 (ruoyi-business)
- BizProject: +is_bid_project, +execOrgNames(GROUP_CONCAT派生), +leadUserName
- BizProjectMapper.xml: 上述字段全部贯通 selectFields/insert/update, 列表加 exec_org_names 子查询 + lead_user_name JOIN
- BizSysUserQueryMapper (新): 干净查 sys_user by role_type, 避开 @DataScope 切面污染, 用于项目负责人下拉
- BizOrgMapper.selectExecutorOrgOptions (新): JOIN sys_user (parent_user_id IS NULL) 限定 MAIN 账号, 排除同公司普通员工子账号
- BizOrgController: +/business/org/executorOptions 端点
- BizExecutionIntentController: +exportSignupExperts 端点 (POST /business/executionIntent/export)
- BizSignupExpertExportVo (新): 中文列头 Excel 导出 VO (专家姓名/科室/医院/职称/报名时间/手机号)
- BizProjectAssign / SponsorAssign 重命名 + Service 实现调整
- 删除 sql/ 下所有迁移脚本 (按用户要求不再保存 SQL 文件)

前端 (ry-vue3)
- api/system.js: +listExecutorOrgs 走新接口, listExecutor/listSupporters/listManagers 保持 listByRole 兼容
- views/manager/Projects.vue: 执行方下拉改 label=u.orgName / value=u.userId, 数据源走 listExecutorOrgs
  +execOrgNames 列展示 +导出报名专家 dropdown 项 +is_bid_project 表单字段
- views/manager/ProjectsNew.vue + ManagerProjectDetail.vue: +is_bid_project 表单/展示
- views/auth/Login.vue: +手机号验证码登录 tab (复用 SysSmsService, dev 模式 10 开头手机号固定码 1234)
- 路由/布局/角色视角多页面整理 (SponsorOrgs/People/SponsorPersonNew 等)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-17 00:34:13 +08:00
co-authored by Claude
parent ccfd03a6eb
commit e099211cbd
70 changed files with 1378 additions and 1505 deletions
@@ -211,6 +211,7 @@ public class BizAuthController extends BaseController {
// 5. 插入 biz_org (executor 类型, 主账号自己当 contact)
BizOrg org = new BizOrg();
org.setOrgId(null);
org.setUserId(userId); // 关联主账号, 让系统能反查 "我的公司"
org.setOrgName(unitName);
org.setOrgType("executor");
org.setBusinessNature(businessNature);
@@ -224,7 +225,7 @@ public class BizAuthController extends BaseController {
}
/**
* 注册赞助方 (主账号), 流程与 executor 一致:
* 注册支持方 (主账号), 流程与 executor 一致:
* 校验 → 查重 → 写 sys_user + role_type=sponsor → 写 biz_org (sponsor 类型)
*/
@PostMapping("/registerSponsor")
@@ -283,6 +284,7 @@ public class BizAuthController extends BaseController {
// 5. 写 biz_org (sponsor 类型)
BizOrg org = new BizOrg();
org.setOrgId(null);
org.setUserId(userId); // 关联主账号, 让系统能反查 "我的公司"
org.setOrgName(unitName);
org.setOrgType("sponsor");
org.setBusinessNature(businessNature);
@@ -1,6 +1,8 @@
package com.ruoyi.business.controller;
import java.util.ArrayList;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
@@ -10,9 +12,11 @@ import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.business.domain.BizExecutionIntent;
import com.ruoyi.business.domain.BizProject;
import com.ruoyi.business.domain.vo.BizSignupExpertExportVo;
import com.ruoyi.business.service.IBizExecutionIntentService;
import com.ruoyi.business.service.IBizProjectService;
@@ -119,4 +123,32 @@ public class BizExecutionIntentController extends BaseController
{
return toAjax(bizExecutionIntentService.deleteByPrimaryKeys(ids));
}
/**
* 导出报名专家 (按 projectNo 过滤, 项目管理-更多-导出报名专家)
* POST /business/executionIntent/export?projectNo=xxx
* 字段: 专家姓名 科室 医院 职称 报名时间 手机号
*/
@Log(title = "导出报名专家", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void exportSignupExperts(HttpServletResponse response, BizExecutionIntent bizExecutionIntent)
{
if (bizExecutionIntent.getProjectNo() == null || bizExecutionIntent.getProjectNo().isEmpty()) {
throw new IllegalArgumentException("projectNo 不能为空");
}
List<BizExecutionIntent> list = bizExecutionIntentService.selectList(bizExecutionIntent);
List<BizSignupExpertExportVo> exportList = new ArrayList<>(list.size());
for (BizExecutionIntent e : list) {
BizSignupExpertExportVo v = new BizSignupExpertExportVo();
v.setName(e.getName());
v.setDepartment(e.getDepartment());
v.setWorkUnit(e.getWorkUnit());
v.setPosition(e.getPosition());
v.setPhone(e.getPhone());
v.setCreateTime(e.getCreateTime());
exportList.add(v);
}
ExcelUtil<BizSignupExpertExportVo> util = new ExcelUtil<>(BizSignupExpertExportVo.class);
util.exportExcel(response, exportList, "报名专家");
}
}
@@ -1,6 +1,7 @@
package com.ruoyi.business.controller;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
@@ -12,7 +13,7 @@ import com.ruoyi.business.domain.BizOrg;
import com.ruoyi.business.service.IBizOrgService;
/**
* 公司Controller (赞助方 + 执行方 共用)
* 公司Controller (支持方 + 执行方 共用)
* GET /business/org/list?orgType=sponsor|executor
*/
@RestController
@@ -28,6 +29,34 @@ public class BizOrgController extends BaseController {
return getDataTable(list);
}
/**
* 支持方下拉选项: JOIN sys_user 取主账号 user_name, 用于 manager 项目分配弹窗
* GET /business/org/sponsorOptions?orgName=xxx
* 返回 [{ userId, orgName, userName }, ...] (不走分页)
*/
@GetMapping("/sponsorOptions")
public AjaxResult sponsorOptions(BizOrg bizOrg) {
// 强制 orgType=sponsor
bizOrg.setOrgType("sponsor");
List<Map<String, Object>> rows = bizOrgService.selectSponsorOrgOptions(bizOrg);
return success(rows);
}
/**
* 执行方下拉选项: JOIN sys_user MAIN 账号 (parent_user_id IS NULL), 用于 manager 项目分配弹窗
* GET /business/org/executorOptions?orgName=xxx
* 返回 [{ userId, orgName, userName }, ...] (不走分页)
* 关键: label=orgName (执行单位名称), value=MAIN user_id (直接写 biz_project_assign.exec_user_id)
* 已通过 SQL 限定 parent_user_id IS NULL, 自动排除同一公司下的普通员工子账号
*/
@GetMapping("/executorOptions")
public AjaxResult executorOptions(BizOrg bizOrg) {
// 强制 orgType=executor
bizOrg.setOrgType("executor");
List<Map<String, Object>> rows = bizOrgService.selectExecutorOrgOptions(bizOrg);
return success(rows);
}
@GetMapping("/{orgId}")
public AjaxResult getInfo(@PathVariable("orgId") Long orgId) {
return success(bizOrgService.getById(orgId));
@@ -20,6 +20,8 @@ import com.ruoyi.business.domain.BizProjectSponsorAssign;
import com.ruoyi.business.service.IBizProjectAssignService;
import com.ruoyi.business.service.IBizProjectRatingService;
import com.ruoyi.business.service.IBizProjectSponsorAssignService;
import com.ruoyi.system.domain.vo.SysUserExtendVo;
import com.ruoyi.business.mapper.BizSysUserQueryMapper;
/**
* 项目Controller
@@ -41,6 +43,8 @@ public class BizProjectController extends BaseController
private IBizProjectRatingService bizProjectRatingService;
@Autowired
private IBizProjectSponsorAssignService bizProjectSponsorAssignService;
@Autowired
private BizSysUserQueryMapper bizSysUserQueryMapper;
/**
* 我报名的项目 (当前用户在 biz_execution_intent 里有意向的项目)
@@ -162,10 +166,10 @@ public class BizProjectController extends BaseController
}
/**
* 赞助方分配监察员 (写 biz_project_sponsor_assign)
* 支持方分配监察员 (写 biz_project_sponsor_assign)
* POST /business/project/sponsorAssign
*/
@Log(title = "赞助方分配监察员", businessType = BusinessType.INSERT)
@Log(title = "支持方分配监察员", businessType = BusinessType.INSERT)
@PostMapping("/sponsorAssign")
public AjaxResult sponsorAssign(@RequestBody BizProjectSponsorAssign body)
{
@@ -176,11 +180,28 @@ public class BizProjectController extends BaseController
}
/**
* 赞助方批量分配监察员 (多个项目, 同一个监察员 + 同一份说明)
* 按 sys_user.role_type 查用户列表 (供前端下拉用, 不走 system:user:list 权限, 限定只查合规管理员等业务角色)
* GET /business/project/listByRole?roleType=manager&userName=xxx
* 不传 roleType 时返回空表, 防止误用全表扫描
* 走业务模块专属 mapper BizSysUserQueryMapper, 避开 system 模块的 @DataScope 切面
*/
@GetMapping("/listByRole")
public TableDataInfo listByRole(SysUserExtendVo vo)
{
if (vo.getRoleType() == null || vo.getRoleType().isEmpty()) {
return new TableDataInfo();
}
startPage();
List<SysUserExtendVo> list = bizSysUserQueryMapper.selectActiveByRole(vo);
return getDataTable(list);
}
/**
* 支持方批量分配监察员 (多个项目, 同一个监察员 + 同一份说明)
* POST /business/project/sponsorAssignBatch
* body: List<BizProjectSponsorAssign> (每个 item.projectId / monitorUserId / assignDesc / assignPoints)
*/
@Log(title = "赞助方批量分配监察员", businessType = BusinessType.INSERT)
@Log(title = "支持方批量分配监察员", businessType = BusinessType.INSERT)
@PostMapping("/sponsorAssignBatch")
public AjaxResult sponsorAssignBatch(@RequestBody List<BizProjectSponsorAssign> bodies)
{
@@ -7,17 +7,20 @@ import com.ruoyi.common.core.domain.BaseEntity;
/**
* 公司对象 biz_org
* 用途: 赞助方(sponsor) + 执行方(executor) 共用
* 用途: 支持方(sponsor) + 执行方(executor) 共用
* 重构说明: 原 biz_support_unit + biz_execution_unit 合并, 通过 org_type 区分
*/
public class BizOrg extends BaseEntity {
private static final long serialVersionUID = 1L;
/** org_id */
private Long orgId;
/** 主账号 sys_user.user_id (注册时自动关联, 一用户一类型一公司) */
@Excel(name = "user_id")
private Long userId;
/** org_name */
@Excel(name = "org_name")
private String orgName;
/** org_type: sponsor赞助方 / executor执行方 */
/** org_type: sponsor支持方 / executor执行方 */
@Excel(name = "org_type")
private String orgType;
/** 企业性质 私营/国营/中外合资/外资/其他 */
@@ -35,7 +38,7 @@ public class BizOrg extends BaseEntity {
/** contact_phone */
@Excel(name = "contact_phone")
private String contactPhone;
/** intent_count (仅赞助方用) */
/** intent_count (仅支持方用) */
@Excel(name = "intent_count")
private Integer intentCount;
/** status */
@@ -50,6 +53,8 @@ public class BizOrg extends BaseEntity {
public Long getOrgId() { return orgId; }
public void setOrgId(Long orgId) { this.orgId = orgId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getOrgName() { return orgName; }
public void setOrgName(String orgName) { this.orgName = orgName; }
public String getOrgType() { return orgType; }
@@ -32,9 +32,6 @@ public class BizPerson extends BaseEntity {
/** role */
@Excel(name = "role")
private String role;
/** 状态 0正常 1禁用 - 跟 sys_user.status 同步, 前端展示请读 sys_user */
@Deprecated
private String status;
/** create_by */
@Excel(name = "create_by")
private String createBy;
@@ -51,6 +48,9 @@ public class BizPerson extends BaseEntity {
private Date updateTime;
/** 关联系统用户ID */
private Long userId;
/** 启停状态 '0'/'1' (前端 toggle 用, BizPersonServiceImpl 同步到 sys_user.status) - 非持久化字段 */
@com.fasterxml.jackson.annotation.JsonProperty("status")
private transient String status;
/** 所属单位类型 execution执行/sponsor支持 */
private String unitType;
/** 账号类型 (来自 sys_user.account_type, MAIN=主账号 / SUB=子账号) - 仅展示用,不入库 */
@@ -81,8 +81,6 @@ public class BizPerson extends BaseEntity {
public void setPosition(String position) { this.position = position; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
/** 兼容前端调用, 实际读 sys_user.delFlag */
public String getDelFlag() { return null; }
public void setDelFlag(String delFlag) { /* noop - 跟 sys_user 同步 */ }
@@ -96,6 +94,8 @@ public class BizPerson extends BaseEntity {
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getUnitType() { return unitType; }
public void setUnitType(String unitType) { this.unitType = unitType; }
public String getAccountType() { return accountType; }
@@ -49,11 +49,15 @@ public class BizProject extends BaseEntity {
private BigDecimal ratingQ3;
/** 评分维度: 合规安全 */
private BigDecimal ratingQ4;
/** 赞助方负责人用户名(冗余) — 原 org_name */
/** 支持方负责人用户名(冗余) — 原 org_name */
@Excel(name = "sponsor_admin_user_name")
private String sponsorAdminUserName;
/** 支持单位名称 (查询参数 + 列表展示列, JOIN biz_org 取 org_name, org_type='sponsor') */
private String sponsorOrgName;
/** 服务机构名称 (查询条件, 仅匹配 org_type='executor' + org_name LIKE) */
private String execOrgName;
/** 服务机构名称列表, 多个用英文逗号连接 (GROUP_CONCAT 派生, 不入库, 仅展示) */
private String execOrgNames;
/** project_form */
@Excel(name = "project_form")
private String projectForm;
@@ -63,9 +67,17 @@ public class BizProject extends BaseEntity {
/** is_settled */
@Excel(name = "is_settled")
private String isSettled;
/** 赞助方负责人用户ID (sys_user.user_id, role_type=sponsor) — 原 org_id */
/** 支持方负责人用户ID (sys_user.user_id, role_type=sponsor) — 原 org_id */
@Excel(name = "sponsor_admin_user_id")
private Long sponsorAdminUserId;
/** 项目负责人 user_id (sys_user.user_id, role_type=manager 合规管理员) */
@Excel(name = "lead_user_id")
private Long leadUserId;
/** 项目负责人用户名 (JOIN sys_user.user_name 派生, 不入库) */
private String leadUserName;
/** 是否招标项目 Y/N */
@Excel(name = "is_bid_project")
private String isBidProject;
/** create_by */
@Excel(name = "create_by")
private String createBy;
@@ -110,7 +122,7 @@ public class BizProject extends BaseEntity {
/** 公告类型 (邀请函/支持函/通知/日程/公示) */
private String announcementType;
/* ============ 赞助方评分字段 (来自 biz_project_sponsor 中间表, 仅展示用) ============ */
/* ============ 支持方评分字段 (来自 biz_project_sponsor 中间表, 仅展示用) ============ */
/** 当前 login 用户对该项目的平均分 */
private BigDecimal sponsorRating;
private Integer sponsorQ1;
@@ -150,8 +162,12 @@ public class BizProject extends BaseEntity {
public void setRatingQ4(BigDecimal ratingQ4) { this.ratingQ4 = ratingQ4; }
public String getSponsorAdminUserName() { return sponsorAdminUserName; }
public void setSponsorAdminUserName(String sponsorAdminUserName) { this.sponsorAdminUserName = sponsorAdminUserName; }
public String getSponsorOrgName() { return sponsorOrgName; }
public void setSponsorOrgName(String sponsorOrgName) { this.sponsorOrgName = sponsorOrgName; }
public String getExecOrgName() { return execOrgName; }
public void setExecOrgName(String execOrgName) { this.execOrgName = execOrgName; }
public String getExecOrgNames() { return execOrgNames; }
public void setExecOrgNames(String execOrgNames) { this.execOrgNames = execOrgNames; }
public String getProjectForm() { return projectForm; }
public void setProjectForm(String projectForm) { this.projectForm = projectForm; }
public String getIsFinished() { return isFinished; }
@@ -160,6 +176,12 @@ public class BizProject extends BaseEntity {
public void setIsSettled(String isSettled) { this.isSettled = isSettled; }
public Long getSponsorAdminUserId() { return sponsorAdminUserId; }
public void setSponsorAdminUserId(Long sponsorAdminUserId) { this.sponsorAdminUserId = sponsorAdminUserId; }
public Long getLeadUserId() { return leadUserId; }
public void setLeadUserId(Long leadUserId) { this.leadUserId = leadUserId; }
public String getLeadUserName() { return leadUserName; }
public void setLeadUserName(String leadUserName) { this.leadUserName = leadUserName; }
public String getIsBidProject() { return isBidProject; }
public void setIsBidProject(String isBidProject) { this.isBidProject = isBidProject; }
public String getCreateBy() { return createBy; }
public void setCreateBy(String createBy) { this.createBy = createBy; }
public Date getCreateTime() { return createTime; }
@@ -3,27 +3,25 @@ package com.ruoyi.business.domain;
import java.math.BigDecimal;
import com.ruoyi.common.core.domain.BaseEntity;
/** 项目执行方分配对象 BizProjectAssign */
/** 项目执行方分配对象 BizProjectAssign
* 注: 不再缓存 execution_unit_name / exec_user_name / exec_nick_name / exec_org,
* 这 4 个字段都是 JOIN 一查就有的快照, 已从 DB DROP。
* 业务展示统一走 service 层 JOIN biz_org + sys_user。
*/
public class BizProjectAssign extends BaseEntity {
private static final long serialVersionUID = 1L;
/** assign_id */
private String assignId;
/** project_id */
private Long projectId;
/** 执行方用户ID (sys_user.user_id) */
/** 执行单位ID (FK: biz_org.org_id, NOT NULL) - service 层从 execUserId 反查 biz_org 写入 */
private Long executionUnitId;
/** 执行方用户ID (sys_user.user_id, executor 主账号) - 业务主键,前端直接传 */
private Long execUserId;
/** 执行方用户名 (denormalized) */
private String execUserName;
/** 执行方昵称 (denormalized) */
private String execNickName;
/** 执行方所属机构 (denormalized) */
private String execOrg;
/** 分配场次 */
private Integer sessions;
/** 分配金额 */
private BigDecimal amount;
/** 备注 */
private String remark;
/** 状态 0正常 1已撤销 */
private String status;
@@ -31,20 +29,14 @@ public class BizProjectAssign extends BaseEntity {
public void setAssignId(String assignId) { this.assignId = assignId; }
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 Long getExecUserId() { return execUserId; }
public void setExecUserId(Long execUserId) { this.execUserId = execUserId; }
public String getExecUserName() { return execUserName; }
public void setExecUserName(String execUserName) { this.execUserName = execUserName; }
public String getExecNickName() { return execNickName; }
public void setExecNickName(String execNickName) { this.execNickName = execNickName; }
public String getExecOrg() { return execOrg; }
public void setExecOrg(String execOrg) { this.execOrg = execOrg; }
public Integer getSessions() { return sessions; }
public void setSessions(Integer sessions) { this.sessions = sessions; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
}
}
@@ -4,7 +4,7 @@ import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
/** 项目-赞助方-监察员分配记录 (biz_project_sponsor_assign) */
/** 项目-支持方-监察员分配记录 (biz_project_sponsor_assign) */
public class BizProjectSponsorAssign extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long id;
@@ -0,0 +1,48 @@
package com.ruoyi.business.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
/**
* 报名专家导出 VO (中文列头)
*
* <p>数据源: biz_execution_intent (公开门户"立即报名"写入的执行意向表, 即已报名专家).
* 仅用于 Excel 导出, 不参与业务逻辑.
*
* @author guoju
*/
public class BizSignupExpertExportVo {
@Excel(name = "专家姓名", sort = 1)
private String name;
@Excel(name = "科室", sort = 2)
private String department;
@Excel(name = "医院", sort = 3)
private String workUnit;
@Excel(name = "职称", sort = 4)
private String position;
@Excel(name = "报名时间", sort = 5, dateFormat = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@Excel(name = "手机号", sort = 6)
private String phone;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public String getWorkUnit() { return workUnit; }
public void setWorkUnit(String workUnit) { this.workUnit = workUnit; }
public String getPosition() { return position; }
public void setPosition(String position) { this.position = position; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
@@ -1,6 +1,7 @@
package com.ruoyi.business.mapper;
import java.util.List;
import java.util.Map;
import com.ruoyi.business.domain.BizOrg;
public interface BizOrgMapper {
@@ -9,4 +10,12 @@ public interface BizOrgMapper {
int insert(BizOrg entity);
int updateByPrimaryKey(BizOrg entity);
int deleteByPrimaryKeys(Long[] orgIds);
/** 支持方下拉选项 (JOIN sys_user.user_name), 用于 manager 项目分配弹窗 */
List<Map<String, Object>> selectSponsorOrgOptions(BizOrg entity);
/** 执行方下拉选项 (JOIN sys_user MAIN 账号 user_name), 用于 manager 项目分配弹窗
* 返回 Map: userId / orgName / userName
* 过滤条件: orgType='executor' + sys_user MAIN 账号 (parent_user_id IS NULL)
* 注意: value 必须 MAIN user_id (写入 biz_project_assign.exec_user_id);
* label 必须 org_name, 不用 user_name 以避免把同公司的普通员工带出来 */
List<Map<String, Object>> selectExecutorOrgOptions(BizOrg entity);
}
@@ -6,6 +6,6 @@ import com.ruoyi.business.domain.BizProjectSponsorAssign;
public interface BizProjectSponsorAssignMapper {
int insertAssign(BizProjectSponsorAssign entity);
List<BizProjectSponsorAssign> selectByProjectId(String projectId);
/** 按 project_id 全删 (赞助方分配: 先删后插策略) */
/** 按 project_id 全删 (支持方分配: 先删后插策略) */
int deleteByProjectId(String projectId);
}
@@ -0,0 +1,22 @@
package com.ruoyi.business.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.ruoyi.system.domain.vo.SysUserExtendVo;
/**
* 业务模块自用的 sys_user 查询 (不走 system 模块的 mapper,
* 避开 SysUserService.selectUserExtendList 上的 @DataScope 切面,
* 因此不能直接复用 /system/user/list 的权限模型)
*
* 用途: 前端下拉按 role_type 选人 (项目负责人 / 执行方 等)
*/
@Mapper
public interface BizSysUserQueryMapper
{
/**
* 按 role_type + 可选 userName 模糊查 sys_user (status='0' 正常, del_flag='0' 未删除)
* 无 dataScope 注入, 所有合规业务角色都可查
*/
List<SysUserExtendVo> selectActiveByRole(SysUserExtendVo vo);
}
@@ -1,6 +1,7 @@
package com.ruoyi.business.service;
import java.util.List;
import java.util.Map;
import com.ruoyi.business.domain.BizOrg;
public interface IBizOrgService {
@@ -9,4 +10,9 @@ public interface IBizOrgService {
int insert(BizOrg entity);
int updateByPrimaryKey(BizOrg entity);
int deleteByPrimaryKeys(Long[] orgIds);
/** 支持方下拉选项 (userId/orgName/userName), 用于 manager 项目分配弹窗 */
List<Map<String, Object>> selectSponsorOrgOptions(BizOrg entity);
/** 执行方下拉选项 (userId/orgName/userName), 用于 manager 项目分配弹窗
* 只返回 biz_org.org_type='executor' 对应的 MAIN 账号 (parent_user_id IS NULL) */
List<Map<String, Object>> selectExecutorOrgOptions(BizOrg entity);
}
@@ -4,7 +4,7 @@ import java.util.List;
import com.ruoyi.business.domain.BizProjectSponsorAssign;
public interface IBizProjectSponsorAssignService {
/** 赞助方分配 (策略: 按 project_id 先删后插, 一个项目只分配一个 sponsor) */
/** 支持方分配 (策略: 按 project_id 先删后插, 一个项目只分配一个 sponsor) */
int insertAssign(BizProjectSponsorAssign entity);
List<BizProjectSponsorAssign> listByProjectId(String projectId);
int deleteByProjectId(String projectId);
@@ -1,6 +1,7 @@
package com.ruoyi.business.service.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.business.domain.BizOrg;
@@ -34,4 +35,14 @@ public class BizOrgServiceImpl implements IBizOrgService {
for (Long id : orgIds) { rows += bizOrgMapper.deleteByPrimaryKeys(new Long[]{id}); }
return rows;
}
@Override
public List<Map<String, Object>> selectSponsorOrgOptions(BizOrg entity) {
return bizOrgMapper.selectSponsorOrgOptions(entity);
}
@Override
public List<Map<String, Object>> selectExecutorOrgOptions(BizOrg entity) {
return bizOrgMapper.selectExecutorOrgOptions(entity);
}
}
@@ -47,20 +47,25 @@ public class BizPersonServiceImpl implements IBizPersonService
throw new ServiceException("登录账号已存在");
}
// 0.5 兜底: 如果前端没传 orgId, 按 (orgName + unitType) 查 biz_org 回填
// (前端从 org 页面跳来时已带 orgId; 直接访问 new 页面没带 orgId 时走这条路径)
if (entity.getOrgId() == null && entity.getOrgName() != null && !entity.getOrgName().isEmpty()
// 0.5 主账号 → 自己公司的 biz_org 反查 (按 user_id + org_type, 一用户一类型一公司)
// 解决: 注册时建了公司但前端拿不到, 子账号瞎填 orgName 报错的问题
if (entity.getOrgId() == null && mainUserId != null
&& entity.getUnitType() != null && !entity.getUnitType().isEmpty()) {
BizOrg q = new BizOrg();
q.setOrgName(entity.getOrgName().trim());
q.setUserId(mainUserId);
q.setOrgType(entity.getUnitType());
List<BizOrg> matched = bizOrgMapper.selectList(q);
if (matched != null && !matched.isEmpty()) {
entity.setOrgId(matched.get(0).getOrgId());
BizOrg mine = matched.get(0);
entity.setOrgId(mine.getOrgId());
// 兜底同步 orgName, 避免 biz_person.org_name 为空
if (entity.getOrgName() == null || entity.getOrgName().isEmpty()) {
entity.setOrgName(mine.getOrgName());
}
}
}
if (entity.getOrgId() == null) {
throw new ServiceException("请填写所属公司 (需先在 [支持单位管理/服务机构管理] 录入 orgType=" + entity.getUnitType() + " 的公司)");
throw new ServiceException("请填写所属公司 (主账号尚未注册公司, 请联系 admin 在 [支持单位管理/服务机构管理] 录入 orgType=" + entity.getUnitType() + " 的公司并关联 user_id)");
}
// 1. 创建 sys_user 子账号
@@ -1,17 +1,25 @@
package com.ruoyi.business.service.impl;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.business.domain.BizOrg;
import com.ruoyi.business.domain.BizProjectAssign;
import com.ruoyi.business.mapper.BizOrgMapper;
import com.ruoyi.business.mapper.BizProjectAssignMapper;
import com.ruoyi.business.service.IBizProjectAssignService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.id.SnowflakeId;
@Service
public class BizProjectAssignServiceImpl implements IBizProjectAssignService
{
@Autowired
private BizProjectAssignMapper bizProjectAssignMapper;
@Autowired
private BizOrgMapper bizOrgMapper;
@Override
public BizProjectAssign getById(String assignId) { return bizProjectAssignMapper.selectByPrimaryKey(assignId); }
@@ -22,19 +30,58 @@ public class BizProjectAssignServiceImpl implements IBizProjectAssignService
@Override
public List<BizProjectAssign> selectList(BizProjectAssign entity) { return bizProjectAssignMapper.selectList(entity); }
/**
* 插入项目执行方分配:
* 1. execUserId → biz_org(org_type='executor') 反查 org_id 写入 executionUnitId (NOT NULL, 找不到 throw)
* 2. 自动补 audit 字段 (createBy/createTime/updateBy/updateTime) 从 SecurityUtils
* 3. SnowflakeId 主键
* 4. status='0' 默认
*/
@Override
public int insert(BizProjectAssign entity) {
com.ruoyi.common.utils.id.SnowflakeId.injectIfEmpty(entity, "assignId");
if (entity.getStatus() == null) entity.setStatus("0");
// 0. 必须有 execUserId (前端必传)
if (entity.getExecUserId() == null) {
throw new ServiceException("执行方用户ID不能为空");
}
// 1. execUserId → biz_org(executor) → executionUnitId
BizOrg q = new BizOrg();
q.setUserId(entity.getExecUserId());
q.setOrgType("executor");
List<BizOrg> matched = bizOrgMapper.selectList(q);
if (matched == null || matched.isEmpty()) {
throw new ServiceException("执行方用户 " + entity.getExecUserId() + " 未关联执行单位 (请检查 biz_org.org_type='executor' + user_id 是否绑定)");
}
BizOrg org = matched.get(0);
entity.setExecutionUnitId(org.getOrgId());
// 2. audit 字段
Date now = new Date();
String operator = SecurityUtils.getUsername();
if (entity.getCreateBy() == null || entity.getCreateBy().isEmpty()) entity.setCreateBy(operator);
if (entity.getCreateTime() == null) entity.setCreateTime(now);
entity.setUpdateBy(operator);
entity.setUpdateTime(now);
// 3. 默认值
if (entity.getStatus() == null || entity.getStatus().isEmpty()) entity.setStatus("0");
// 4. SnowflakeId
SnowflakeId.injectIfEmpty(entity, "assignId");
return bizProjectAssignMapper.insert(entity);
}
@Override
public int updateByPrimaryKey(BizProjectAssign entity) { return bizProjectAssignMapper.updateByPrimaryKey(entity); }
public int updateByPrimaryKey(BizProjectAssign entity) {
entity.setUpdateBy(SecurityUtils.getUsername());
entity.setUpdateTime(new Date());
return bizProjectAssignMapper.updateByPrimaryKey(entity);
}
@Override
public int deleteByPrimaryKey(String assignId) { return bizProjectAssignMapper.deleteByPrimaryKey(assignId); }
@Override
public int deleteByProjectId(Long projectId) { return bizProjectAssignMapper.deleteByProjectId(projectId); }
}
}
@@ -14,7 +14,7 @@ public class BizProjectSponsorAssignServiceImpl implements IBizProjectSponsorAss
@Override
public int insertAssign(BizProjectSponsorAssign entity) {
// 赞助方分配策略: 一个项目只分配一个 sponsor, 先按 project_id 删, 再插
// 支持方分配策略: 一个项目只分配一个 sponsor, 先按 project_id 删, 再插
mapper.deleteByProjectId(entity.getProjectId());
return mapper.insertAssign(entity);
}
@@ -3,6 +3,7 @@
<mapper namespace="com.ruoyi.business.mapper.BizOrgMapper">
<resultMap type="BizOrg" id="BizOrgResult">
<id property="orgId" column="org_id" />
<result property="userId" column="user_id" />
<result property="orgName" column="org_name" />
<result property="orgType" column="org_type" />
<result property="businessNature" column="business_nature" />
@@ -19,7 +20,7 @@
</resultMap>
<sql id="selectFields">
select org_id, org_name, org_type, business_nature, address, tax_no, status, create_time,
select org_id, user_id, org_name, org_type, business_nature, address, tax_no, status, create_time,
contact_name, contact_phone, intent_count,
create_by, update_by, update_time
from biz_org
@@ -33,6 +34,7 @@
<select id="selectList" resultMap="BizOrgResult" parameterType="BizOrg">
<include refid="selectFields"/>
<where>
<if test="userId != null"> and user_id = #{userId}</if>
<if test="orgType != null and orgType != ''"> and org_type = #{orgType}</if>
<if test="orgName != null and orgName != ''"> and org_name like concat('%', #{orgName}, '%')</if>
<if test="taxNo != null and taxNo != ''"> and tax_no = #{taxNo}</if>
@@ -44,6 +46,7 @@
<insert id="insert" parameterType="BizOrg" useGeneratedKeys="true" keyProperty="orgId">
insert into biz_org
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="orgName != null and orgName != ''">org_name,</if>
<if test="orgType != null and orgType != ''">org_type,</if>
<if test="businessNature != null and businessNature != ''">business_nature,</if>
@@ -57,6 +60,7 @@
create_time,
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="orgName != null and orgName != ''">#{orgName},</if>
<if test="orgType != null and orgType != ''">#{orgType},</if>
<if test="businessNature != null and businessNature != ''">#{businessNature},</if>
@@ -74,6 +78,7 @@
<update id="updateByPrimaryKey" parameterType="BizOrg">
update biz_org
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null">user_id = #{userId},</if>
<if test="orgName != null and orgName != ''">org_name = #{orgName},</if>
<if test="orgType != null and orgType != ''">org_type = #{orgType},</if>
<if test="businessNature != null and businessNature != ''">business_nature = #{businessNature},</if>
@@ -95,4 +100,46 @@
#{orgId}
</foreach>
</delete>
<!--
支持方下拉选项: JOIN sys_user 取主账号 user_name (供分配弹窗缓存 biz_project.sponsor_admin_user_name 用)
返回 Map: userId / orgName / userName
走 idx_org_user_type_name 索引 (user_id, org_type, org_name)
过滤条件: orgName 模糊匹配 (主用) / userId 精确匹配 (拉回已选项)
-->
<select id="selectSponsorOrgOptions" parameterType="BizOrg" resultType="java.util.LinkedHashMap">
select o.user_id as userId,
o.org_name as orgName,
u.user_name as userName
from biz_org o
join sys_user u on u.user_id = o.user_id
where o.org_type = 'sponsor'
and u.del_flag = '0'
<if test="userId != null">and o.user_id = #{userId}</if>
<if test="orgName != null and orgName != ''">and o.org_name like concat('%', #{orgName}, '%')</if>
order by o.org_id desc
</select>
<!--
执行方下拉选项: JOIN sys_user 取 MAIN 账号 user_name (parent_user_id IS NULL 限定主账号)
返回 Map: userId / orgName / userName
走 idx_org_user_type_name 索引 (user_id, org_type, org_name)
关键: 只查 MAIN 账号 (parent_user_id IS NULL), 过滤掉同一公司下的普通员工子账号
value=userId (MAIN 账号 sys_user.user_id, 直接写 biz_project_assign.exec_user_id)
label=orgName (执行单位名称, 不带 user_name 避免人名/昵称混淆)
过滤条件: orgName 模糊匹配 (主用) / userId 精确匹配 (拉回已选项)
-->
<select id="selectExecutorOrgOptions" parameterType="BizOrg" resultType="java.util.LinkedHashMap">
select o.user_id as userId,
o.org_name as orgName,
u.user_name as userName
from biz_org o
join sys_user u on u.user_id = o.user_id
where o.org_type = 'executor'
and u.del_flag = '0'
and u.parent_user_id is null
<if test="userId != null">and o.user_id = #{userId}</if>
<if test="orgName != null and orgName != ''">and o.org_name like concat('%', #{orgName}, '%')</if>
order by o.org_id desc
</select>
</mapper>
@@ -11,7 +11,6 @@
<result property="department" column="department" />
<result property="position" column="position" />
<result property="role" column="role" />
<result property="status" column="status" />
<result property="userId" column="user_id" />
<result property="unitType" column="unit_type" />
<result property="accountType" column="account_type" />
@@ -2,25 +2,24 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.business.mapper.BizProjectAssignMapper">
<resultMap type="BizProjectAssign" id="BizProjectAssignResult">
<id property="assignId" column="assign_id" />
<result property="projectId" column="project_id" />
<result property="execUserId" column="exec_user_id" />
<result property="execUserName" column="exec_user_name" />
<result property="execNickName" column="exec_nick_name" />
<result property="execOrg" column="exec_org" />
<result property="sessions" column="sessions" />
<result property="amount" column="amount" />
<result property="remark" column="remark" />
<result property="status" column="status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<id property="assignId" column="assign_id" />
<result property="projectId" column="project_id" />
<result property="executionUnitId" column="execution_unit_id" />
<result property="execUserId" column="exec_user_id" />
<result property="sessions" column="sessions" />
<result property="amount" column="amount" />
<result property="remark" column="remark" />
<result property="status" column="status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectFields">
select assign_id, project_id, exec_user_id, exec_user_name, exec_nick_name, exec_org,
sessions, amount, remark, status, create_by, create_time, update_by, update_time
select assign_id, project_id, execution_unit_id, exec_user_id,
sessions, amount, remark, status,
create_by, create_time, update_by, update_time
from biz_project_assign
</sql>
@@ -39,57 +38,60 @@
<include refid="selectFields"/>
<where>
<if test="projectId != null"> and project_id = #{projectId}</if>
<if test="executionUnitId != null"> and execution_unit_id = #{executionUnitId}</if>
<if test="execUserId != null"> and exec_user_id = #{execUserId}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
</where>
order by assign_id desc
</select>
<!--
INSERT: execution_unit_id 由 service 层从 exec_user_id 反查 biz_org 写入 (NOT NULL)
audit 字段 createBy/createTime/updateBy/updateTime 由 service 层从 SecurityUtils 写入
-->
<insert id="insert" parameterType="BizProjectAssign">
insert into biz_project_assign
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="assignId != null">assign_id,</if>
<if test="assignId != null and assignId != ''">assign_id,</if>
<if test="projectId != null">project_id,</if>
<if test="executionUnitId != null">execution_unit_id,</if>
<if test="execUserId != null">exec_user_id,</if>
<if test="execUserName != null and execUserName != ''">exec_user_name,</if>
<if test="execNickName != null and execNickName != ''">exec_nick_name,</if>
<if test="execOrg != null and execOrg != ''">exec_org,</if>
<if test="sessions != null">sessions,</if>
<if test="amount != null">amount,</if>
<if test="remark != null and remark != ''">remark,</if>
<if test="status != null and status != ''">status,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
create_time,
<if test="createTime != null">create_time,</if>
<if test="updateBy != null and updateBy != ''">update_by,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="assignId != null">#{assignId},</if>
<if test="assignId != null and assignId != ''">#{assignId},</if>
<if test="projectId != null">#{projectId},</if>
<if test="executionUnitId != null">#{executionUnitId},</if>
<if test="execUserId != null">#{execUserId},</if>
<if test="execUserName != null and execUserName != ''">#{execUserName},</if>
<if test="execNickName != null and execNickName != ''">#{execNickName},</if>
<if test="execOrg != null and execOrg != ''">#{execOrg},</if>
<if test="sessions != null">#{sessions},</if>
<if test="amount != null">#{amount},</if>
<if test="remark != null and remark != ''">#{remark},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
sysdate(),
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null and updateBy != ''">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<update id="updateByPrimaryKey" parameterType="BizProjectAssign">
update biz_project_assign
<trim prefix="SET" suffixOverrides=",">
<if test="executionUnitId != null">execution_unit_id = #{executionUnitId},</if>
<if test="execUserId != null">exec_user_id = #{execUserId},</if>
<if test="execUserName != null and execUserName != ''">exec_user_name = #{execUserName},</if>
<if test="execNickName != null and execNickName != ''">exec_nick_name = #{execNickName},</if>
<if test="execOrg != null and execOrg != ''">exec_org = #{execOrg},</if>
<if test="sessions != null">sessions = #{sessions},</if>
<if test="amount != null">amount = #{amount},</if>
<if test="remark != null and remark != ''">remark = #{remark},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
update_time = sysdate(),
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim>
where assign_id = #{assignId}
</update>
@@ -101,4 +103,4 @@
<delete id="deleteByProjectId" parameterType="Long">
delete from biz_project_assign where project_id = #{projectId}
</delete>
</mapper>
</mapper>
@@ -13,16 +13,16 @@
<result property="paidLaborAmount" column="paid_labor_amount" />
<result property="paidMeetingAmount" column="paid_meeting_amount" />
<result property="ratingScore" column="rating_score" />
<result property="ratingQ1" column="rating_q1" />
<result property="ratingQ2" column="rating_q2" />
<result property="ratingQ3" column="rating_q3" />
<result property="ratingQ4" column="rating_q4" />
<result property="sponsorAdminUserName" column="sponsor_admin_user_name" />
<result property="sponsorAdminUserId" column="sponsor_admin_user_id" />
<result property="leadUserId" column="lead_user_id" />
<result property="leadUserName" column="lead_user_name" />
<result property="isBidProject" column="is_bid_project" />
<result property="sponsorOrgName" column="sponsor_org_name" />
<result property="execOrgNames" column="exec_org_names" />
<result property="projectForm" column="project_form" />
<result property="isFinished" column="is_finished" />
<result property="isSettled" column="is_settled" />
<result property="sponsorAdminUserId" column="sponsor_admin_user_id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
@@ -36,35 +36,39 @@
<result property="invitationUrl" column="invitation_url" />
<result property="supportLetterUrl" column="support_letter_url" />
<result property="publishUrl" column="publish_url" />
<result property="noticeUrl" column="notice_url" />
<result property="scheduleUrl" column="schedule_url" />
<result property="isPublished" column="is_published" />
<result property="publishTime" column="publish_time" />
<result property="announcementType" column="announcement_type" />
</resultMap>
<sql id="selectFields">
select project_id, project_no, project_name, total_sessions, done_sessions, todo_sessions, total_amount, available_amount, paid_labor_amount, paid_meeting_amount, rating_score, rating_q1, rating_q2, rating_q3, rating_q4, sponsor_admin_user_name, project_form, is_finished, is_settled, sponsor_admin_user_id, create_by, create_time, update_by, update_time, manage_fee, start_time, end_time, submit_deadline_days, support_contract_url, execute_contract_url, invitation_url, support_letter_url, publish_url, notice_url, schedule_url, is_published, publish_time, announcement_type
from biz_project
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.rating_score, p.sponsor_admin_user_name, p.project_form, p.is_finished, p.is_settled, p.sponsor_admin_user_id, p.lead_user_id, p.is_bid_project, p.create_by, p.create_time, p.update_by, p.update_time, p.manage_fee, 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,
lu.user_name as lead_user_name,
(select group_concat(distinct o2.org_name separator ',')
from biz_project_assign bpa
join biz_org o2 on o2.org_id = bpa.execution_unit_id and o2.org_type = 'executor'
where bpa.project_id = p.project_id) as exec_org_names
from biz_project p
left join biz_org o on o.user_id = p.sponsor_admin_user_id and o.org_type = 'sponsor'
left join sys_user lu on lu.user_id = p.lead_user_id
</sql>
<!-- sponsor 端专属查询: 字段 = selectFields + LEFT JOIN 当前 login 用户对该项目的评分 -->
<!-- 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.sponsor_admin_user_name, p.project_form, p.is_finished, p.is_settled,
p.sponsor_admin_user_id,
p.rating_score, p.sponsor_admin_user_name, p.project_form, p.is_finished, p.is_settled,
p.sponsor_admin_user_id, p.lead_user_id, p.is_bid_project,
p.create_by, p.create_time, p.update_by, p.update_time, p.manage_fee,
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.notice_url, p.schedule_url, p.is_published, p.publish_time, p.announcement_type,
bps.sponsor_rating as sponsor_rating,
bps.sponsor_q1 as sponsor_q1,
bps.sponsor_q2 as sponsor_q2,
bps.sponsor_q3 as sponsor_q3,
bps.sponsor_q4 as sponsor_q4,
bps.sponsor_remark as sponsor_remark
p.publish_url,
o.org_name as sponsor_org_name,
lu.user_name as lead_user_name,
(select group_concat(distinct o2.org_name separator ',')
from biz_project_assign bpa
join biz_org o2 on o2.org_id = bpa.execution_unit_id and o2.org_type = 'executor'
where bpa.project_id = p.project_id) as exec_org_names
from biz_project p
left join biz_project_sponsor bps on bps.project_id = p.project_id and bps.user_id = #{params.loginUid}
left join biz_org o on o.user_id = p.sponsor_admin_user_id and o.org_type = 'sponsor'
left join sys_user lu on lu.user_id = p.lead_user_id
</sql>
<select id="selectByPrimaryKey" resultMap="BizProjectResult" parameterType="String">
<include refid="selectFields"/>
@@ -107,29 +111,28 @@
<if test="projectNo != null and projectNo != ''">and project_no like concat('%', #{projectNo}, '%')</if>
<if test="projectName != null and projectName != ''">and project_name like concat('%', #{projectName}, '%')</if>
<if test="projectForm != null and projectForm != ''">and project_form = #{projectForm}</if>
<if test="sponsorAdminUserName != null and sponsorAdminUserName != ''">
<if test="sponsorOrgName != null and sponsorOrgName != ''">
and exists (
select 1 from sys_user su
where su.user_id = sponsor_admin_user_id
and su.role_type = 'sponsor'
and (su.user_name like concat('%', #{sponsorAdminUserName}, '%')
or su.nick_name like concat('%', #{sponsorAdminUserName}, '%'))
select 1 from biz_org o
where o.user_id = sponsor_admin_user_id
and o.org_type = 'sponsor'
and o.org_name like concat('%', #{sponsorOrgName}, '%')
)
</if>
<if test="execOrgName != null and execOrgName != ''">
and exists (
select 1 from biz_project_assign bpa
join biz_org o2 on o2.org_id = bpa.execution_unit_id and o2.org_type = 'executor'
join sys_user su on su.user_id = bpa.exec_user_id
where bpa.project_id = project_id
and su.role_type = 'executor'
and (su.user_name like concat('%', #{execOrgName}, '%')
or su.nick_name like concat('%', #{execOrgName}, '%')
or bpa.exec_org like concat('%', #{execOrgName}, '%'))
and (o2.org_name like concat('%', #{execOrgName}, '%')
or su.user_name like concat('%', #{execOrgName}, '%')
or su.nick_name like concat('%', #{execOrgName}, '%'))
)
</if>
<if test="isFinished != null and isFinished != ''">and is_finished = #{isFinished}</if>
<if test="isSettled != null and isSettled != ''">and is_settled = #{isSettled}</if>
<if test="isPublished != null and isPublished != ''">and is_published = #{isPublished}</if>
</where>
order by project_id desc
</select>
@@ -147,15 +150,13 @@
<if test="paidLaborAmount != null">paid_labor_amount,</if>
<if test="paidMeetingAmount != null">paid_meeting_amount,</if>
<if test="ratingScore != null">rating_score,</if>
<if test="ratingQ1 != null">rating_q1,</if>
<if test="ratingQ2 != null">rating_q2,</if>
<if test="ratingQ3 != null">rating_q3,</if>
<if test="ratingQ4 != null">rating_q4,</if>
<if test="sponsorAdminUserName != null and sponsorAdminUserName != ''">sponsor_admin_user_name,</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="sponsorAdminUserId != null">sponsor_admin_user_id,</if>
<if test="leadUserId != null">lead_user_id,</if>
<if test="isBidProject != null and isBidProject != ''">is_bid_project,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
@@ -169,11 +170,6 @@
<if test="invitationUrl != null and invitationUrl != ''">invitation_url,</if>
<if test="supportLetterUrl != null and supportLetterUrl != ''">support_letter_url,</if>
<if test="publishUrl != null and publishUrl != ''">publish_url,</if>
<if test="noticeUrl != null and noticeUrl != ''">notice_url,</if>
<if test="scheduleUrl != null and scheduleUrl != ''">schedule_url,</if>
<if test="isPublished != null and isPublished != ''">is_published,</if>
<if test="publishTime != null">publish_time,</if>
<if test="announcementType != null and announcementType != ''">announcement_type,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="projectId != null and projectId != ''">#{projectId},</if>
@@ -187,15 +183,13 @@
<if test="paidLaborAmount != null">#{paidLaborAmount},</if>
<if test="paidMeetingAmount != null">#{paidMeetingAmount},</if>
<if test="ratingScore != null">#{ratingScore},</if>
<if test="ratingQ1 != null">#{ratingQ1},</if>
<if test="ratingQ2 != null">#{ratingQ2},</if>
<if test="ratingQ3 != null">#{ratingQ3},</if>
<if test="ratingQ4 != null">#{ratingQ4},</if>
<if test="sponsorAdminUserName != null and sponsorAdminUserName != ''">#{sponsorAdminUserName},</if>
<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="sponsorAdminUserId != null">#{sponsorAdminUserId},</if>
<if test="leadUserId != null">#{leadUserId},</if>
<if test="isBidProject != null and isBidProject != ''">#{isBidProject},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
@@ -209,11 +203,6 @@
<if test="invitationUrl != null and invitationUrl != ''">#{invitationUrl},</if>
<if test="supportLetterUrl != null and supportLetterUrl != ''">#{supportLetterUrl},</if>
<if test="publishUrl != null and publishUrl != ''">#{publishUrl},</if>
<if test="noticeUrl != null and noticeUrl != ''">#{noticeUrl},</if>
<if test="scheduleUrl != null and scheduleUrl != ''">#{scheduleUrl},</if>
<if test="isPublished != null and isPublished != ''">#{isPublished},</if>
<if test="publishTime != null">#{publishTime},</if>
<if test="announcementType != null and announcementType != ''">#{announcementType},</if>
</trim>
</insert>
<update id="updateByPrimaryKey" parameterType="BizProject">
@@ -228,11 +217,6 @@
<if test="invitationUrl != null and invitationUrl != ''">invitation_url = #{invitationUrl},</if>
<if test="supportLetterUrl != null and supportLetterUrl != ''">support_letter_url = #{supportLetterUrl},</if>
<if test="publishUrl != null and publishUrl != ''">publish_url = #{publishUrl},</if>
<if test="noticeUrl != null and noticeUrl != ''">notice_url = #{noticeUrl},</if>
<if test="scheduleUrl != null and scheduleUrl != ''">schedule_url = #{scheduleUrl},</if>
<if test="isPublished != null and isPublished != ''">is_published = #{isPublished},</if>
<if test="publishTime != null">publish_time = #{publishTime},</if>
<if test="announcementType != null and announcementType != ''">announcement_type = #{announcementType},</if>
<if test="projectNo != null and projectNo != ''">project_no = #{projectNo},</if>
<if test="projectName != null and projectName != ''">project_name = #{projectName},</if>
<if test="totalSessions != null">total_sessions = #{totalSessions},</if>
@@ -243,15 +227,13 @@
<if test="paidLaborAmount != null">paid_labor_amount = #{paidLaborAmount},</if>
<if test="paidMeetingAmount != null">paid_meeting_amount = #{paidMeetingAmount},</if>
<if test="ratingScore != null">rating_score = #{ratingScore},</if>
<if test="ratingQ1 != null">rating_q1 = #{ratingQ1},</if>
<if test="ratingQ2 != null">rating_q2 = #{ratingQ2},</if>
<if test="ratingQ3 != null">rating_q3 = #{ratingQ3},</if>
<if test="ratingQ4 != null">rating_q4 = #{ratingQ4},</if>
<if test="sponsorAdminUserName != null and sponsorAdminUserName != ''">sponsor_admin_user_name = #{sponsorAdminUserName},</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="sponsorAdminUserId != null">sponsor_admin_user_id = #{sponsorAdminUserId},</if>
<if test="leadUserId != null">lead_user_id = #{leadUserId},</if>
<if test="isBidProject != null and isBidProject != ''">is_bid_project = #{isBidProject},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
@@ -22,7 +22,7 @@
(#{projectId}, #{sponsorUserId}, #{monitorUserId}, #{assignDesc}, #{assignPoints}, #{createBy}, sysdate())
</insert>
<!-- 按 project_id 全删 (赞助方分配策略: 一个项目只分配一个 sponsor, 先删后插) -->
<!-- 按 project_id 全删 (支持方分配策略: 一个项目只分配一个 sponsor, 先删后插) -->
<delete id="deleteByProjectId" parameterType="String">
delete from biz_project_sponsor_assign where project_id = #{projectId}
</delete>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.business.mapper.BizSysUserQueryMapper">
<resultMap type="SysUserExtendVo" id="BizSysUserResult">
<id property="userId" column="user_id" />
<result property="userName" column="user_name" />
<result property="nickName" column="nick_name" />
<result property="accountType" column="account_type" />
<result property="roleType" column="role_type" />
<result property="phonenumber" column="phonenumber" />
<result property="email" column="email" />
<result property="status" column="status" />
</resultMap>
<!--
业务模块专用: 按 sys_user.role_type 查用户列表 (项目负责人 / 执行方 下拉)
不注入 ${params.dataScope}, 避免被当前用户权限过滤 (manager 没 system:user:list 会被清空)
限定 status='0' + del_flag='0', 排除停用/删除账号
-->
<select id="selectActiveByRole" parameterType="SysUserExtendVo" resultMap="BizSysUserResult">
select u.user_id, u.user_name, u.nick_name, u.account_type, u.role_type,
u.phonenumber, u.email, u.status
from sys_user u
where u.del_flag = '0'
and u.status = '0'
and u.role_type = #{roleType}
<if test="userId != null and userId != 0">
AND u.user_id = #{userId}
</if>
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
order by u.user_id asc
</select>
</mapper>