体育活动创建可以沿用,承办单位选填;导出出现重复人员修复;优化增加外援功能,也可以导出;报名界面的设置领导以及教练不能删除修复

This commit is contained in:
2026-09-28 17:43:46 +08:00
parent 39bdcbfc14
commit b2fc1979fe
18 changed files with 1062 additions and 430 deletions
@@ -349,6 +349,7 @@ public class ActivityApplyController {
*/
@At
@ViReturn
@Aop(TransAop.READ_COMMITTED)
public Object doApply(@Param("data") ActivitySchoolApply[] applies, @Param("appliedData3") ActivitySchoolApply[] applies2, String eventId, boolean pass) {
Sys_user user = (Sys_user) ShiroUtil.getPrincipal();
Sys_unit unit = user.getUnit();
@@ -376,16 +377,40 @@ public class ActivityApplyController {
apply2.setActivityUnionId(basicUnit.getUnionid());
apply2.setActivityUnionName(basicUnion.getUnionname());
}
if (applies.length > 0) {
activitySchoolApplyService.doApplies(activityId, basicUnit.getUnionid(), eventId, Arrays.asList(applies));
}
if (applies2.length > 0) {
activitySchoolApplyService.doApplies(activityId, basicUnit.getUnionid(), eventId, Arrays.asList(applies2));
if (applies.length > 0 || applies2.length > 0) {
List<ActivitySchoolApply> allApplies = new ArrayList<>(Arrays.asList(applies));
allApplies.addAll(Arrays.asList(applies2));
activitySchoolApplyService.doApplies(activityId, basicUnit.getUnionid(), eventId, allApplies);
}
return null;
}
/**
* 创建报名专用外援,工号由服务端生成,页面不能指定。
* @param username 外援姓名
* @param sex 性别,男或女
* @param birthday 出生日期,yyyy-MM-dd
* @param mobile 手机号码
* @return Sys_user,id 为报名人员 ID,loginname 为 wy 年份流水工号,其他字段用于页面回显
*/
@At
@ViReturn
@RequiresPermissions("activity.apply")
@Aop(TransAop.READ_COMMITTED)
public Object addExternalUser(String username, String sex, String birthday, String mobile) {
Sys_user operator = (Sys_user) ShiroUtil.getPrincipal();
return activitySchoolApplyService.createExternalUser(username, sex, birthday, mobile, operator.getUnitid());
}
/** @return String,当前年份的下一个外援工号预览,实际编号以创建接口返回值为准。 */
@At
@ViReturn
@RequiresPermissions("activity.apply")
public Object previewExternalLoginname() {
return activitySchoolApplyService.previewExternalLoginname();
}
/**
* 个人报名
*
@@ -646,116 +671,39 @@ public class ActivityApplyController {
/**
* 如果当前项目是综合类活动就添加工会总领队和教练
* 保存综合类活动的领队、教练及工作人员;取消选择的活动级身份会删除。
*
* @param LeaderData
* @param CoachData
* @return
* @param activityId 当前活动ID
* @param staffData 工作人员报名信息,userId 为所选用户ID,空值表示取消
* @param LeaderData 领队报名信息,userId 为所选用户ID,空值表示取消
* @param CoachData 教练报名信息,userId 为所选用户ID,空值表示取消
* @param headData 团长报名信息,userId 为所选用户ID,空值表示取消
* @return null,由 @ViReturn 包装为操作结果
*/
@At
@ViReturn
public Object doLeaderCoachHead(@Param("staffData") ActivitySchoolApply staffData, @Param("LeaderData") ActivitySchoolApply LeaderData, @Param("CoachData") ActivitySchoolApply CoachData, @Param("headData") ActivitySchoolApply headData) {
if (staffData.getUserId() != null) {
ActivityBasicUnit basicUnit = activitySchoolService.dao().fetch(ActivityBasicUnit.class, Cnd.where("id", "=", staffData.getUnitId()));
ActivityBasicUnion basicUnion = activitySchoolService.dao().fetch(ActivityBasicUnion.class, Cnd.where("id", "=", basicUnit.getUnionid()));
activitySchoolApplyService.clear(Cnd.where("activityId", "=", staffData.getActivityId()).and("unionStaff", "=", true).and("unionId", "=", getUnionId()));
staffData.setApplyUser(ShiroUtil.getPrincipalProperty("id").toString());
staffData.setApplyDate(DateUtil.getDate());
staffData.setUnitname(basicUnit.getName());
staffData.setActivityUnionId(basicUnion.getId());
staffData.setActivityUnionName(basicUnion.getUnionname());
activitySchoolApplyService.insert(staffData);
}
if (LeaderData.getUserId() != null) {
ActivityBasicUnit basicUnit = activitySchoolService.dao().fetch(ActivityBasicUnit.class, Cnd.where("id", "=", LeaderData.getUnitId()));
ActivityBasicUnion basicUnion = activitySchoolService.dao().fetch(ActivityBasicUnion.class, Cnd.where("id", "=", basicUnit.getUnionid()));
activitySchoolApplyService.clear(Cnd.where("activityId", "=", LeaderData.getActivityId()).and("unionLeader", "=", true).and("unionId", "=", getUnionId()));
LeaderData.setApplyUser(ShiroUtil.getPrincipalProperty("id").toString());
LeaderData.setApplyDate(DateUtil.getDate());
LeaderData.setUnitname(basicUnit.getName());
LeaderData.setActivityUnionId(basicUnion.getId());
LeaderData.setActivityUnionName(basicUnion.getUnionname());
activitySchoolApplyService.insert(LeaderData);
}
if (CoachData.getUserId() != null) {
ActivityBasicUnit basicUnit = activitySchoolService.dao().fetch(ActivityBasicUnit.class, Cnd.where("id", "=", CoachData.getUnitId()));
ActivityBasicUnion basicUnion = activitySchoolService.dao().fetch(ActivityBasicUnion.class, Cnd.where("id", "=", basicUnit.getUnionid()));
activitySchoolApplyService.clear(Cnd.where("activityId", "=", CoachData.getActivityId()).and("unionCoach", "=", true).and("unionId", "=", getUnionId()));
CoachData.setApplyUser(ShiroUtil.getPrincipalProperty("id").toString());
CoachData.setApplyDate(DateUtil.getDate());
CoachData.setUnitname(basicUnit.getName());
CoachData.setActivityUnionId(basicUnion.getId());
CoachData.setActivityUnionName(basicUnion.getUnionname());
activitySchoolApplyService.insert(CoachData);
}
if (headData.getUserId() != null) {
ActivityBasicUnit basicUnit = activitySchoolService.dao().fetch(ActivityBasicUnit.class, Cnd.where("id", "=", headData.getUnitId()));
ActivityBasicUnion basicUnion = activitySchoolService.dao().fetch(ActivityBasicUnion.class, Cnd.where("id", "=", basicUnit.getUnionid()));
activitySchoolApplyService.clear(Cnd.where("activityId", "=", headData.getActivityId()).and("unionHead", "=", true).and("unionId", "=", getUnionId()));
headData.setApplyUser(ShiroUtil.getPrincipalProperty("id").toString());
headData.setApplyDate(DateUtil.getDate());
headData.setUnitname(basicUnit.getName());
headData.setActivityUnionId(basicUnion.getId());
headData.setActivityUnionName(basicUnion.getUnionname());
activitySchoolApplyService.insert(headData);
}
@Aop(TransAop.READ_COMMITTED)
public Object doLeaderCoachHead(String activityId, @Param("staffData") ActivitySchoolApply staffData,
@Param("LeaderData") ActivitySchoolApply LeaderData,
@Param("CoachData") ActivitySchoolApply CoachData,
@Param("headData") ActivitySchoolApply headData) {
activitySchoolApplyService.saveUnionRoles(activityId, getUnionId(), ShiroUtil.getUserId(),
staffData, LeaderData, CoachData, headData);
return null;
}
/**
* 查询活动中当前工会的领队、教练和工作人员,并提供可回显的用户选项。
*
* @param activityId 当前活动ID
* @return Map,unionCoach、unionLeader、unionHead、unionStaff 为对应报名记录,
* userOptions 为有效用户列表;历史空 userId 仅在返回值中按唯一工号回显
*/
@At
@ViReturn
public Object getUnionCoachLeaderHead(String activityId) {
Map result = new HashMap();
Sql sql = Sqls.create("""
SELECT apply.*,us.unitname FROM activity_school_apply apply LEFT JOIN `user` us ON us.id=apply.userId WHERE apply.activityId=@activityId and apply.unionId=@unionId
""");
sql.setParam("unionId", getUnionId());
sql.setParam("activityId", activityId);
List<NutMap> list = activitySchoolApplyService.listMap(sql);
NutMap unionCoach = list.stream().filter(v -> {
return v.getBoolean("unionCoach");
}).findFirst().orElse(null);
NutMap unionLeader = list.stream().filter(v -> {
return v.getBoolean("unionLeader");
}).findFirst().orElse(null);
NutMap unionHead = list.stream().filter(v -> {
return v.getBoolean("unionHead");
}).findFirst().orElse(null);
NutMap unionStaff = list.stream().filter(v -> {
return v.getBoolean("unionStaff");
}).findFirst().orElse(null);
result.put("unionCoach", unionCoach);
result.put("unionLeader", unionLeader);
result.put("unionHead", unionHead);
result.put("unionStaff", unionStaff);
List userOptions = List.of();
ArrayList arrayList = new ArrayList<>(userOptions);
if (unionCoach != null) {
arrayList.add(sysUserService.fetch(unionCoach.getString("userId")));
}
if (unionLeader != null) {
arrayList.add(sysUserService.fetch(unionLeader.getString("userId")));
}
if (unionHead != null) {
arrayList.add(sysUserService.fetch(unionHead.getString("userId")));
}
if (unionStaff != null) {
arrayList.add(sysUserService.fetch(unionStaff.getString("userId")));
}
result.put("userOptions", arrayList);
return result;
return activitySchoolApplyService.getUnionRoleInfo(activityId, getUnionId());
}
@At
@@ -2,6 +2,7 @@ package io.v.nutz.activity.controller.school;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.XWPFTemplate;
@@ -206,7 +207,7 @@ public class ActivityCommonController {
SELECT
app.*,
team.`name` team,
unit.`name` unitname
COALESCE(NULLIF(app.unitname, ''), unit.`name`) unitname
FROM
activity_school_apply app
LEFT JOIN activity_school_team team ON app.teamId = team.id
@@ -266,7 +267,14 @@ public class ActivityCommonController {
public void exportXlsx(String id, String unionId, HttpServletResponse response) throws IOException {
Sql sql = Sqls.create("""
SELECT
app.id,
app.activityId,
app.unionId,
app.userId,
app.eventId,
app.teamId,
app.username,
app.birthday,
COALESCE(NULLIF(app.loginname, ''), u.loginname) loginname,
app.identity,
app.activityUnionName,
@@ -316,7 +324,7 @@ public class ActivityCommonController {
if (Strings.isNotBlank(unionId)) {
sql.setVar("cnd", "and u.unionid='" + unionId + "'");
}
List<NutMap> nutMaps = activitySchoolApplyService.listMap(sql);
List<NutMap> nutMaps = activitySchoolApplyService.mergeExportIdentities(activitySchoolApplyService.listMap(sql));
String activityName = nutMaps.get(0).getString("activityName");
List<ExcelExportEntity> entityList = new ArrayList<>();
@@ -329,6 +337,7 @@ public class ActivityCommonController {
entityList.add(new ExcelExportEntity("工号", "loginname", 20));
entityList.add(new ExcelExportEntity("姓名", "username", 20));
entityList.add(new ExcelExportEntity("性别", "sex", 10));
entityList.add(new ExcelExportEntity("出生年月", "birthday", 20));
entityList.add(new ExcelExportEntity("身份证号", "idcard", 20));
entityList.add(new ExcelExportEntity("报名人电话号码", "mobile", 20));
entityList.add(new ExcelExportEntity("是否会员", "isMember", 15));
@@ -343,8 +352,10 @@ public class ActivityCommonController {
z.put("sf", sf.replace("[", "").replace("]", ""));
});
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + new String((activityName + "报名表.xls").getBytes("utf-8"), "ISO8859-1"));
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entityList, nutMaps);
response.setHeader("Content-Disposition", "attachment;filename=" + new String((activityName + "报名表.xlsx").getBytes("utf-8"), "ISO8859-1"));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, nutMaps);
markMissingRequiredInfo(workbook);
workbook.write(response.getOutputStream());
}
@@ -454,7 +465,7 @@ public class ActivityCommonController {
ae.allName,
app.*,
team.`name` teamName,
unit.`name` unitName,
COALESCE(NULLIF(app.unitname, ''), unit.`name`) unitName,
gh.unionname AS unionName
FROM
activity_school_apply app
@@ -640,7 +651,7 @@ public class ActivityCommonController {
SELECT
app.*,
team.`name` team,
unit.`name` unitname
COALESCE(NULLIF(app.unitname, ''), unit.`name`) unitname
FROM
activity_school_apply app
LEFT JOIN activity_school_team team ON app.teamId = team.id
@@ -16,6 +16,8 @@ import io.v.nutz.util.ShiroUtil;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Chain;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
@@ -57,6 +59,38 @@ public class ActivityInfoManageController {
public void index() {
}
/** 无参数;返回 data 数组,每项包含可沿用活动的 id 和 name。 */
@At
@ViReturn
@RequiresPermissions("activity.school.info")
public Object getReuseActivityOptions() {
return activitySchoolService.getReuseActivityOptions();
}
/** id 为来源活动ID;返回 data 对象,包含活动字段和 schoolEvents 项目配置数组。 */
@At
@ViReturn
@RequiresPermissions("activity.school.info")
public Object findReuseActivity(String id) {
return activitySchoolService.findReuseActivity(id);
}
/**
* sourceId 为来源活动ID,data 为新活动表单,events 为项目配置数组。
* 返回 data 字符串(新活动ID),供保存后继续编辑使用。
*/
@At
@ViReturn
@RequiresPermissions("activity.school.info")
@Aop(TransAop.READ_COMMITTED)
public Object doReuseAdd(String sourceId, @Param("data") ActivitySchool data,
@Param("events") ActivitySchoolEvent[] events) {
if (StrUtil.isBlank(sourceId) || data == null || data.getActivityLevel() == null) {
return Result.error("请选择沿用活动并填写活动信息");
}
return activitySchoolService.doReuseAdd(sourceId, data, events);
}
@At
@ViReturn
public Object pageData(PageForm page, Integer year, String name, String gameStyle, String query, String activityId, String activityLevel) {
@@ -121,7 +121,7 @@ public class ActivityReadingController {
school.belongUnionId belongUnionId,
school.belongClubId belongClubId,
team.`name` team,
unit.`name` unitname,
COALESCE(NULLIF(app.unitname, ''), unit.`name`) unitname,
ev.allName,
ev.id evid,
ev.projectType
@@ -230,6 +230,9 @@ public class ActivityReadingController {
Sql sql = Sqls.create("""
SELECT
apply.loginname,
apply.birthday,
apply.mobile,
apply.unitname,
apply.username,
apply.sex,
IF(apply.sex='男',1,2) AS sexOrder,
@@ -269,6 +272,10 @@ public class ActivityReadingController {
for (int i = 0; i < v.size(); i++) {
switch (i) {
case 0 -> {
entity.setLoginname(v.get(i).getString("loginname"));
entity.setBirthday(v.get(i).getString("birthday"));
entity.setMobile(v.get(i).getString("mobile"));
entity.setUnitname(v.get(i).getString("unitname"));
if (v.get(i).getInt("awardsMode") == 1) {
entity.setUserName(v.get(i).getString("username"));
entity.setSex(v.get(i).getString("sex"));
@@ -52,6 +52,9 @@ public class ActivityReviewController {
apply.loginname,
apply.username,
apply.mobile,
apply.sex,
apply.birthday,
apply.unitname,
apply.identity,
school.id schoolId,
school.eventsId,
@@ -18,6 +18,14 @@ public class SchoolActivityApplyExportEntity {
private String number;
@Excel(name = "姓名", width = 20d)
private String userName;
@Excel(name = "工号", width = 20d)
private String loginname;
@Excel(name = "出生年月", width = 20d)
private String birthday;
@Excel(name = "电话", width = 20d)
private String mobile;
@Excel(name = "单位", width = 20d)
private String unitname;
@Excel(name = "性别", width = 20d)
private String sex;
@@ -1,18 +1,66 @@
package io.v.nutz.activity.services;
import io.v.nutz.activity.models.ActivitySchoolApply;
import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.service.ViService;
import org.nutz.lang.util.NutMap;
import java.util.List;
import java.util.Map;
/**
* @author Aaron
*/
public interface ActivitySchoolApplyService extends ViService<ActivitySchoolApply> {
/** @return 当前年份预计分配的外援工号;实际工号以创建接口返回值为准。 */
String previewExternalLoginname();
/**
* 创建仅用于报名的外援用户,按年份生成 wy+年份+流水号的工号。
* @param username 外援姓名
* @param sex 性别,男或女
* @param birthday 出生日期,yyyy-MM-dd
* @param mobile 手机号码
* @param unitId 报名人所在单位 ID,用于归属当前报名工会
* @return 已创建的系统用户,包含用户 ID 和自动生成的工号
*/
Sys_user createExternalUser(String username, String sex, String birthday, String mobile, String unitId);
void doApplies(List<ActivitySchoolApply> applies);
void doApplies(String activity, String union_id, String eventId,List<ActivitySchoolApply> applies);
void doApply(ActivitySchoolApply apply);
/**
* 保存活动级工会人员;空 userId 表示取消该身份,项目内的报名记录不受影响。
*
* @param activityId 活动ID
* @param unionId 当前工会ID
* @param operatorId 当前操作人的用户ID
* @param staffData 工作人员报名信息,空 userId 表示取消
* @param leaderData 领队报名信息,空 userId 表示取消
* @param coachData 教练报名信息,空 userId 表示取消
* @param headData 团长报名信息,空 userId 表示取消
*/
void saveUnionRoles(String activityId, String unionId, String operatorId, ActivitySchoolApply staffData,
ActivitySchoolApply leaderData, ActivitySchoolApply coachData, ActivitySchoolApply headData);
/**
* 查询活动级工会人员及姓名下拉选项,历史空用户ID仅在返回数据中按唯一工号补齐。
*
* @param activityId 活动ID
* @param unionId 当前工会ID
* @return 包含 unionCoach、unionLeader、unionHead、unionStaff 报名记录及 userOptions 用户列表的 Map
*/
Map<String, Object> getUnionRoleInfo(String activityId, String unionId);
/**
* 合并同一人员在活动级和同一项目队伍中的导出身份,保留不同项目和队伍。
*
* @param rows 导出查询的报名记录,需包含活动、工会、人员、项目、队伍ID及 identity
* @return 每个项目队伍一行的导出记录;没有项目报名的活动级人员仍保留一行
*/
List<NutMap> mergeExportIdentities(List<NutMap> rows);
}
@@ -4,12 +4,23 @@ package io.v.nutz.activity.services;
import io.v.nutz.activity.models.ActivitySchool;
import io.v.nutz.activity.models.ActivitySchoolEvent;
import io.v.nutz.service.ViService;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* @author Aaron
*/
public interface ActivitySchoolService extends ViService<ActivitySchool> {
/** 无参数;返回当前管理范围内可沿用活动的 id、name 列表,按创建时间倒序。 */
List<NutMap> getReuseActivityOptions();
/** id 为历史活动ID;返回活动字段及 schoolEvents 项目配置数组,不可访问时抛出异常。 */
NutMap findReuseActivity(String id);
/** sourceId 为来源活动ID,activitySchool 为新活动信息,events 为项目配置;返回新活动ID。 */
String doReuseAdd(String sourceId, ActivitySchool activitySchool, ActivitySchoolEvent[] events);
String doAdd(ActivitySchool activitySchool, ActivitySchoolEvent[] events);
void doEdit(ActivitySchool activitySchool, ActivitySchoolEvent[] events, String applyEndTime);
@@ -1,25 +1,93 @@
package io.v.nutz.activity.services.impl;
import io.v.nutz.activity.models.ActivityBasicUnion;
import io.v.nutz.activity.models.ActivityBasicUnit;
import io.v.nutz.activity.models.ActivitySchoolApply;
import io.v.nutz.activity.services.ActivitySchoolApplyService;
import io.v.nutz.base.utils.DateUtil;
import io.v.nutz.service.impl.ViServiceImpl;
import io.v.nutz.sys.models.Sys_user;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import java.time.LocalDate;
import java.time.Year;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@IocBean(args = {"refer:dao"})
public class ActivitySchoolApplyServiceImpl extends ViServiceImpl<ActivitySchoolApply> implements ActivitySchoolApplyService {
private static final Object EXTERNAL_NUMBER_LOCK = new Object();
public ActivitySchoolApplyServiceImpl(Dao dao) {
super(dao);
}
@Override
public String previewExternalLoginname() {
String prefix = "wy" + Year.now().getValue();
Sql next = Sqls.create("SELECT COALESCE(MAX(CAST(SUBSTRING(loginname, 7) AS UNSIGNED)), 0) "
+ "FROM sys_user WHERE loginname REGEXP @pattern");
next.setParam("pattern", "^" + prefix + "[0-9]+$");
next.setCallback(Sqls.callback.integer());
dao().execute(next);
int number = ((Number) next.getResult()).intValue() + 1;
return prefix + String.format("%02d", number);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public Sys_user createExternalUser(String username, String sex, String birthday, String mobile, String unitId) {
String name = Strings.sNull(username).trim();
String phone = Strings.sNull(mobile).trim();
if (name.isEmpty() || name.length() > 50 || !("男".equals(sex) || "女".equals(sex))
|| !phone.matches("^1[3456789]\\d{9}$")) {
throw new IllegalArgumentException("请填写外援姓名、性别及正确的手机号码");
}
try {
LocalDate.parse(birthday);
} catch (DateTimeParseException | NullPointerException e) {
throw new IllegalArgumentException("请选择正确的出生年月");
}
ActivityBasicUnit unit = dao().fetch(ActivityBasicUnit.class, unitId);
if (unit == null || Strings.isBlank(unit.getUnionid())) {
throw new IllegalArgumentException("当前报名单位未配置活动工会");
}
// 工号在服务端分配,避免多个浏览器按相同的前端预览号建人。
synchronized (EXTERNAL_NUMBER_LOCK) {
Sys_user user = new Sys_user();
user.setId(R.UU32());
user.setLoginname(previewExternalLoginname());
user.setUsername(name);
user.setSex(sex);
user.setBirthday(birthday);
user.setMobile(phone);
user.setUnitid(unitId);
user.setUnionid(unit.getUnionid());
user.setDisabled(true);
dao().insert(user);
return user;
}
}
@Aop(TransAop.READ_COMMITTED)
public void doApplies(List<ActivitySchoolApply> applies) {
@@ -32,6 +100,20 @@ public class ActivitySchoolApplyServiceImpl extends ViServiceImpl<ActivitySchool
@Override
@Aop(TransAop.READ_COMMITTED)
public void doApplies(String activity, String union_id, String eventId,List<ActivitySchoolApply> applies) {
for (ActivitySchoolApply apply : applies) {
Sys_user user = Strings.isBlank(apply.getUserId()) ? null : dao().fetch(Sys_user.class, apply.getUserId());
if (user != null && user.isDisabled() && Strings.isNotBlank(user.getLoginname())
&& user.getLoginname().matches("wy[0-9]{6,}")) {
// 外援资料以创建时保存的用户记录为准,不接受浏览器修改工号或单位名称。
apply.setLoginname(user.getLoginname());
apply.setUsername(user.getUsername());
apply.setSex(user.getSex());
apply.setBirthday(user.getBirthday());
apply.setMobile(user.getMobile());
apply.setUnitId(user.getUnitid());
apply.setUnitname("外援");
}
}
clear(Cnd.where("activityId", "=", activity).and("activityUnionId", "=", union_id).and("eventId","=",eventId));
doApplies(applies);
}
@@ -41,4 +123,159 @@ public class ActivitySchoolApplyServiceImpl extends ViServiceImpl<ActivitySchool
public void doApply(ActivitySchoolApply apply) {
insert(apply);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void saveUnionRoles(String activityId, String unionId, String operatorId, ActivitySchoolApply staffData,
ActivitySchoolApply leaderData, ActivitySchoolApply coachData, ActivitySchoolApply headData) {
saveUnionRole(activityId, unionId, operatorId, staffData, "unionStaff");
saveUnionRole(activityId, unionId, operatorId, leaderData, "unionLeader");
saveUnionRole(activityId, unionId, operatorId, coachData, "unionCoach");
saveUnionRole(activityId, unionId, operatorId, headData, "unionHead");
}
/**
* 仅替换活动级身份。先清除旧记录,使空 userId 能表示取消,同时不碰项目队伍身份。
*/
@Aop(TransAop.READ_COMMITTED)
private void saveUnionRole(String activityId, String unionId, String operatorId, ActivitySchoolApply role, String roleColumn) {
clear(Cnd.where("activityId", "=", activityId).and("unionId", "=", unionId)
.and("eventId", "IS", null).and(roleColumn, "=", true));
if (role == null || Strings.isBlank(role.getUserId())) {
return;
}
Sys_user user = dao().fetch(Sys_user.class, role.getUserId());
ActivityBasicUnit unit = dao().fetch(ActivityBasicUnit.class, role.getUnitId());
if (user == null || unit == null || Strings.isBlank(unit.getUnionid())) {
throw new IllegalArgumentException("所选人员或单位不存在,请重新选择");
}
ActivityBasicUnion union = dao().fetch(ActivityBasicUnion.class, unit.getUnionid());
if (union == null) {
throw new IllegalArgumentException("所选人员的活动工会不存在");
}
role.setActivityId(activityId);
role.setEventId(null);
role.setTeamId(null);
role.setUnionId(unionId);
role.setApplyUser(operatorId);
role.setApplyDate(DateUtil.getDate());
role.setUnitname(unit.getName());
role.setActivityUnionId(union.getId());
role.setActivityUnionName(union.getUnionname());
role.setUsername(user.getUsername());
role.setLoginname(user.getLoginname());
if ("unionStaff".equals(roleColumn)) role.setUnionStaff(true);
if ("unionLeader".equals(roleColumn)) role.setUnionLeader(true);
if ("unionCoach".equals(roleColumn)) role.setUnionCoach(true);
if ("unionHead".equals(roleColumn)) role.setUnionHead(true);
insert(role);
}
@Override
public Map<String, Object> getUnionRoleInfo(String activityId, String unionId) {
Sql sql = Sqls.create("SELECT apply.*,us.unitname FROM activity_school_apply apply "
+ "LEFT JOIN `user` us ON us.id=apply.userId "
+ "WHERE apply.activityId=@activityId AND apply.unionId=@unionId AND apply.eventId IS NULL");
sql.setParam("activityId", activityId);
sql.setParam("unionId", unionId);
List<NutMap> rows = listMap(sql);
NutMap coach = rows.stream().filter(row -> Boolean.TRUE.equals(row.getBoolean("unionCoach"))).findFirst().orElse(null);
NutMap leader = rows.stream().filter(row -> Boolean.TRUE.equals(row.getBoolean("unionLeader"))).findFirst().orElse(null);
NutMap head = rows.stream().filter(row -> Boolean.TRUE.equals(row.getBoolean("unionHead"))).findFirst().orElse(null);
NutMap staff = rows.stream().filter(row -> Boolean.TRUE.equals(row.getBoolean("unionStaff"))).findFirst().orElse(null);
Map<String, Object> result = new LinkedHashMap<>();
result.put("unionCoach", coach);
result.put("unionLeader", leader);
result.put("unionHead", head);
result.put("unionStaff", staff);
Map<String, Sys_user> options = new LinkedHashMap<>();
// 历史记录的 userId 可能为空;工号只在唯一匹配时用于回显,不在查询时改写数据库。
for (NutMap role : Arrays.asList(coach, leader, head, staff)) {
if (role == null) continue;
Sys_user user = Strings.isBlank(role.getString("userId")) ? null : dao().fetch(Sys_user.class, role.getString("userId"));
if (user == null && Strings.isNotBlank(role.getString("loginname"))) {
List<Sys_user> matches = dao().query(Sys_user.class, Cnd.where("loginname", "=", role.getString("loginname")));
if (matches.size() == 1) user = matches.get(0);
}
if (user != null) {
role.put("userId", user.getId());
options.put(user.getId(), user);
}
}
result.put("userOptions", new ArrayList<>(options.values()));
return result;
}
@Override
public List<NutMap> mergeExportIdentities(List<NutMap> rows) {
Map<String, Set<String>> accountUsers = new LinkedHashMap<>();
for (NutMap row : rows) {
if (Strings.isNotBlank(row.getString("loginname")) && Strings.isNotBlank(row.getString("userId"))) {
accountUsers.computeIfAbsent(exportAccountKey(row), key -> new LinkedHashSet<>()).add(row.getString("userId"));
}
}
Map<String, Set<String>> activityRoles = new LinkedHashMap<>();
Map<String, Set<String>> projectRoles = new LinkedHashMap<>();
Map<String, NutMap> projectRows = new LinkedHashMap<>();
Set<String> peopleWithProjects = new LinkedHashSet<>();
for (NutMap row : rows) {
String person = exportPersonKey(row, accountUsers);
List<String> identity = Lang.map2Object(row, ActivitySchoolApply.class).getIdentity();
String project = exportProjectKey(row, person);
if (project == null) {
activityRoles.computeIfAbsent(person, key -> new LinkedHashSet<>()).addAll(identity == null ? List.of() : identity);
} else {
peopleWithProjects.add(person);
projectRows.putIfAbsent(project, row);
projectRoles.computeIfAbsent(project, key -> new LinkedHashSet<>()).addAll(identity == null ? List.of() : identity);
}
}
List<NutMap> result = new ArrayList<>();
Set<String> emitted = new LinkedHashSet<>();
for (NutMap row : rows) {
String person = exportPersonKey(row, accountUsers);
String project = exportProjectKey(row, person);
if (project == null && peopleWithProjects.contains(person)) continue;
String outputKey = project == null ? "activity:" + person : "project:" + project;
if (!emitted.add(outputKey)) continue;
NutMap source = project == null ? row : projectRows.get(project);
NutMap merged = new NutMap();
merged.putAll(source);
Set<String> identities = new LinkedHashSet<>();
if (project != null) identities.addAll(projectRoles.get(project));
identities.addAll(activityRoles.getOrDefault(person, Set.of()));
merged.put("identity", new ArrayList<>(identities));
result.add(merged);
}
return result;
}
/** 工号和姓名用于关联历史空 userId 记录;发现多个账号共用该组合时,保留各自记录。 */
private String exportPersonKey(NutMap row, Map<String, Set<String>> accountUsers) {
String union = Strings.sNull(row.getString("unionId"));
String loginname = row.getString("loginname");
String userId = row.getString("userId");
if (Strings.isNotBlank(loginname)) {
String account = exportAccountKey(row);
if (accountUsers.getOrDefault(account, Set.of()).size() > 1) {
return Strings.isNotBlank(userId) ? union + ":user:" + userId : union + ":row:" + row.getString("id");
}
return account;
}
if (Strings.isNotBlank(userId)) return union + ":user:" + userId;
return union + ":row:" + row.getString("id");
}
private String exportAccountKey(NutMap row) {
return Strings.sNull(row.getString("unionId")) + ":login:" + row.getString("loginname")
+ ":name:" + Strings.sNull(row.getString("username"));
}
/** 同一项目中不同队伍仍是不同报名行;活动级身份没有项目和队伍。 */
private String exportProjectKey(NutMap row, String person) {
if (Strings.isBlank(row.getString("eventId"))) return null;
return person + ":event:" + row.getString("eventId") + ":team:" + Strings.sNull(row.getString("teamId"));
}
}
@@ -6,11 +6,18 @@ import io.v.nutz.activity.services.ActivitySchoolApplyService;
import io.v.nutz.activity.services.ActivitySchoolService;
import io.v.nutz.activity.services.ActivitySchoolTeamService;
import io.v.nutz.base.utils.DateUtil;
import io.v.nutz.base.utils.Vi;
import io.v.nutz.activity.utils.ActivityRoleUtil;
import io.v.nutz.util.ShiroUtil;
import io.v.nutz.service.ViService;
import io.v.nutz.service.impl.ViServiceImpl;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Chain;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -46,6 +53,114 @@ public class ActivitySchoolServiceImpl extends ViServiceImpl<ActivitySchool> imp
@Inject("ActivitySchoolEvent")
private ViService<ActivitySchoolEvent> activitySchoolEventViService;
@Inject
private Vi vi;
/** 沿用列表和详情共用现有活动管理的数据范围,防止通过历史活动ID跨范围读取。 */
private Cnd reuseActivityCnd() {
Cnd cnd = Cnd.NEW();
if (!ShiroUtil.hasAnyRoles("sysadmin,A06")) {
if (ActivityRoleUtil.isUnionManager()) {
cnd.and("belongUnionId", "in", ActivityRoleUtil.getManageUnionStr(ShiroUtil.getUserId()));
}
if (ShiroUtil.hasRole("H02")) {
cnd.and("belongClubId", "in", vi.getMangeClubStr());
}
}
return cnd;
}
/** 返回当前用户管理范围内的历史活动ID和名称,按创建时间倒序排列。 */
@Override
public List<NutMap> getReuseActivityOptions() {
return listMap(Sqls.create("SELECT id, name FROM activity_school $condition")
.setCondition(reuseActivityCnd().desc("foundDate")));
}
/** id 为历史活动ID;详情包含原活动字段和 schoolEvents 数组,供前端复制业务配置。 */
@Override
public NutMap findReuseActivity(String id) {
if (Strings.isBlank(id) || fetch(reuseActivityCnd().and("id", "=", id)) == null) {
throw new IllegalArgumentException("沿用活动不存在或无权访问");
}
NutMap result = fetch(Sqls.create("SELECT * FROM activity_school WHERE id = @id").setParam("id", id));
result.setv("schoolEvents", listMap(Sqls.create("SELECT ase.*, ae.projectType FROM activity_school_event ase "
+ "LEFT JOIN activity_event ae ON ae.id = ase.eventId WHERE ase.activityId = @id").setParam("id", id)));
return result;
}
/**
* sourceId 为来源活动ID;只复制表单及项目配置,重新生成活动、配置、小队和组织记录。
* 返回新活动ID,所有写入使用同一事务,任何失败均回滚,不修改来源活动及报名记录。
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public String doReuseAdd(String sourceId, ActivitySchool school, ActivitySchoolEvent[] events) {
findReuseActivity(sourceId);
if (school.getEventsIds() == null || school.getEventsIds().isEmpty()) {
throw new IllegalArgumentException("请选择活动项目");
}
ActivitySchoolEvent[] cleanEvents = Arrays.stream(events == null ? new ActivitySchoolEvent[0] : events)
.filter(event -> event != null && school.getEventsIds().contains(event.getEventId())
&& !"allItems".equals(event.getEventId()))
.peek(event -> {
// 配置主键和所属活动必须重新生成,不能信任前端传入的旧记录标识。
event.setId(null);
event.setActivityId(null);
}).toArray(ActivitySchoolEvent[]::new);
for (String eventId : school.getEventsIds()) {
ActivityEvent event = dao().fetch(ActivityEvent.class, Cnd.where("id", "=", eventId).and("whetherEnable", "=", 1));
if (event == null) {
throw new IllegalArgumentException("所选项目已停用或不存在,请重新选择");
}
if (Arrays.stream(cleanEvents).noneMatch(config -> eventId.equals(config.getEventId()))) {
throw new IllegalArgumentException("项目配置不完整,请进入下一步完善项目信息");
}
}
school.setId(null);
school.setClose(false);
school.setFoundDate(DateUtil.getDate());
if (school.getActivityLevel() == 40002) {
school.setBelongUnionId(ActivityRoleUtil.getCurrentUnionId(dao()));
} else {
school.setBelongUnionId(null);
}
if (school.getUndertakeUnit() == null) {
school.setUndertakeUnit(new java.util.ArrayList<>());
}
insert(school);
addTeam(school.getId(), cleanEvents);
// 与普通创建保持一致,创建可报名工会和活动组织记录,保留后续报名入口所需关联。
for (ActivityBasicUnion unit : dao().query(ActivityBasicUnion.class, Cnd.where("isEnable", "=", true))) {
ActivityUnion union = new ActivityUnion();
union.setActivityId(school.getId());
union.setUnionname(unit.getUnionname());
dao().insert(union);
}
ActivityTissue tissue = new ActivityTissue();
tissue.setApplyTime(school.getFoundDate());
tissue.setType(school.getActivityLevel());
tissue.setProjectTypeCode("50004");
tissue.setActivityCode(school.getActivityCode());
tissue.setName(school.getName());
tissue.setUserId(ShiroUtil.getUserId());
tissue.setState(3);
tissue.setClubId(school.getBelongClubId());
tissue.setUnionId(school.getBelongUnionId());
tissue.setAddress(school.getAddress());
tissue.setApplyStartTime(school.getApplyStartTime());
tissue.setApplyEndTime(school.getApplyEndTime());
tissue.setStartTime(school.getStartTime());
tissue.setEndTime(school.getEndTime());
if (school.getImage() != null && !school.getImage().isEmpty()) {
tissue.setCover(school.getImage().get(0).getId());
}
dao().insert(tissue);
// 与原创建入口一致,在插入后统一组织ID,避免实体 @Prev 的主键生成策略影响关联。
dao().update(ActivityTissue.class, Chain.make("id", school.getId()), Cnd.where("id", "=", tissue.getId()));
return school.getId();
}
@Override
@Aop(TransAop.READ_COMMITTED)
public String doAdd(ActivitySchool activitySchool, ActivitySchoolEvent[] events) {