diff --git a/src/main/java/com/budwk/app/sys/services/SysFileService.java b/src/main/java/com/budwk/app/sys/services/SysFileService.java index 9976d46..7211350 100644 --- a/src/main/java/com/budwk/app/sys/services/SysFileService.java +++ b/src/main/java/com/budwk/app/sys/services/SysFileService.java @@ -25,6 +25,12 @@ public interface SysFileService extends BaseService { */ String uploadReturnUrl(String engine, TempFile file); + /** + * 文件字节上传,返回文件Url。 + * 参数 engine 为存储引擎,fileName 为带后缀的文件名,bytes 为文件内容;返回值为前端可直接预览的下载地址。 + */ + String uploadBytesReturnUrl(String engine, String fileName, byte[] bytes); + /** * 分页 */ diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java index 92f35e8..2d0a43c 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java @@ -65,6 +65,11 @@ public class SysFileServiceImpl extends BaseServiceImpl implements Sys return this.storageFile(engine, file, false); } + @Override + public String uploadBytesReturnUrl(String engine, String fileName, byte[] bytes) { + return this.storageFile(engine, fileName, bytes, false); + } + @Override public Pagination page(Sys_file file) { return null; diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/controller/ActivityFriendshipSignupController.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/controller/ActivityFriendshipSignupController.java new file mode 100644 index 0000000..21b5c91 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/controller/ActivityFriendshipSignupController.java @@ -0,0 +1,90 @@ +package com.budwk.app.zhgh.activity.friendship.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.budwk.app.base.annotation.SLog; +import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.result.Result; +import com.budwk.app.zhgh.activity.friendship.models.ActivityFriendshipSignup; +import com.budwk.app.zhgh.activity.friendship.service.ActivityFriendshipSignupService; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.AdaptBy; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; +import org.nutz.mvc.upload.TempFile; +import org.nutz.mvc.upload.UploadAdaptor; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; + +@IocBean +@Ok("json:full") +@At("/platform/activity/friendship/signup") +public class ActivityFriendshipSignupController { + + @Inject + private ActivityFriendshipSignupService activityFriendshipSignupService; + + @At("") + @Ok("beetl:/platform/zhgh/activity/friendship/index.html") + @SaCheckPermission("activity.friendship.manage") + public void index() { + } + + @At + @SaCheckPermission("activity.friendship.manage") + public Result pageData(PageForm pageForm, String sex, String maritalStatus, String education, String post, String personnelType, String unionId, String unitId) { + return Result.success(activityFriendshipSignupService.pageData(pageForm, sex, maritalStatus, education, post, personnelType, unionId, unitId)); + } + + @At + @SaCheckPermission("activity.friendship.manage") + public Result findOne(@Valid String id) { + return Result.success(activityFriendshipSignupService.fetch(id)); + } + + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("activity.friendship.manage") + @SLog(tag = "单身联谊", msg = "新增报名台账") + public Result insert(@Param("data") @Valid ActivityFriendshipSignup signup) { + activityFriendshipSignupService.insertSignup(signup); + return Result.success(); + } + + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("activity.friendship.manage") + @SLog(tag = "单身联谊", msg = "修改报名台账") + public Result update(@Param("data") @Valid ActivityFriendshipSignup signup) { + activityFriendshipSignupService.updateSignup(signup); + return Result.success(); + } + + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("activity.friendship.manage") + @SLog(tag = "单身联谊", msg = "删除报名台账") + public Result delete(@Valid String id) { + activityFriendshipSignupService.delete(id); + return Result.success(); + } + + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("activity.friendship.manage") + @AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"}) + public Result doImport(@Param("file") TempFile file, @Param("isFlag") Boolean isFlag) { + return activityFriendshipSignupService.doImport(file, isFlag); + } + + @At + @Ok("void") + @SaCheckPermission("activity.friendship.manage") + public void exportExcel(HttpServletResponse response) { + activityFriendshipSignupService.exportExcel(response); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/controller/H5ActivityFriendshipController.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/controller/H5ActivityFriendshipController.java new file mode 100644 index 0000000..faea855 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/controller/H5ActivityFriendshipController.java @@ -0,0 +1,30 @@ +package com.budwk.app.zhgh.activity.friendship.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.budwk.app.base.result.Result; +import com.budwk.app.zhgh.activity.friendship.service.ActivityFriendshipSignupService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +@IocBean +@Ok("json:full") +@At("/platform/h5/activity/friendship") +public class H5ActivityFriendshipController { + + @Inject + private ActivityFriendshipSignupService activityFriendshipSignupService; + + @At("") + @Ok("beetl:/platform/zhghh5/activity/friendship/index.html") + @SaCheckPermission("h5.activity.friendship.view") + public void index() { + } + + @At + @SaCheckPermission("h5.activity.friendship.view") + public Result listCards() { + return Result.success(activityFriendshipSignupService.listMobileCards()); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipExcelMode.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipExcelMode.java new file mode 100644 index 0000000..35fb50c --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipExcelMode.java @@ -0,0 +1,80 @@ +package com.budwk.app.zhgh.activity.friendship.mode; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 单身联谊 Excel 导出结构。 + */ +@Data +public class ActivityFriendshipExcelMode { + + @Excel(name = "编号", width = 15) + private String signupNo; + + @Excel(name = "工号", width = 20) + private String loginName; + + @Excel(name = "姓名", width = 20) + private String username; + + @Excel(name = "所属工会", width = 25) + private String unionName; + + @Excel(name = "所属单位", width = 30) + private String unitName; + + @Excel(name = "报名时间", width = 25) + private String signupTime; + + @Excel(name = "性别", width = 10) + private String sex; + + @Excel(name = "婚况", width = 15) + private String maritalStatus; + + @Excel(name = "学历", width = 15) + private String education; + + @Excel(name = "岗位", width = 20) + private String post; + + @Excel(name = "人事编制", width = 20) + private String personnelType; + + @Excel(name = "出生年份", width = 15) + private String birthYear; + + @Excel(name = "户籍(市)", width = 20) + private String householdCity; + + @Excel(name = "家乡(市)", width = 20) + private String hometownCity; + + @Excel(name = "身高(厘米)", width = 15) + private String heightCm; + + @Excel(name = "体重(千克)", width = 15) + private String weightKg; + + @Excel(name = "年收入(万)", width = 15) + private String annualIncome; + + @Excel(name = "车房", width = 25) + private String carHouse; + + @Excel(name = "自我描述", width = 40) + private String selfDescription; + + @Excel(name = "家庭情况", width = 40) + private String familyInfo; + + @Excel(name = "兴趣爱好", width = 40) + private String hobbies; + + @Excel(name = "择偶意向", width = 40) + private String matePreference; + + @Excel(name = "生活照(不展示)", width = 40) + private String photoUrl; +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipImportError.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipImportError.java new file mode 100644 index 0000000..c76ad11 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipImportError.java @@ -0,0 +1,18 @@ +package com.budwk.app.zhgh.activity.friendship.mode; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 单身联谊 Excel 导入错误行。 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ActivityFriendshipImportError { + + private int rowNo; + private String loginName; + private String errorInfo; +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipImportResult.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipImportResult.java new file mode 100644 index 0000000..638526d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/mode/ActivityFriendshipImportResult.java @@ -0,0 +1,18 @@ +package com.budwk.app.zhgh.activity.friendship.mode; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 单身联谊 Excel 导入结果。 + */ +@Data +public class ActivityFriendshipImportResult { + + private int totalCount; + private int successCount; + private int errorCount; + private List errorList = new ArrayList<>(); +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/models/ActivityFriendshipSignup.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/models/ActivityFriendshipSignup.java new file mode 100644 index 0000000..fc3c20e --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/models/ActivityFriendshipSignup.java @@ -0,0 +1,130 @@ +package com.budwk.app.zhgh.activity.friendship.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.entity.annotation.TableMeta; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +/** + * 单身联谊报名台账。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("activity_friendship_signup") +@TableMeta("{'mysql-charset':'utf8mb4'}") +@Comment("单身联谊报名台账") +public class ActivityFriendshipSignup extends BaseModel { + + @Name + @Comment("ID") + @PrevInsert(uu32 = true) + @ColDefine(type = ColType.VARCHAR, width = 32) + private String id; + + @Column + @Comment("报名编号") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String signupNo; + + @Column + @Comment("工号") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String loginName; + + @Column + @Comment("报名时间") + @ColDefine(type = ColType.VARCHAR, width = 30) + private String signupTime; + + @Column + @Comment("性别") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String sex; + + @Column + @Comment("婚况") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String maritalStatus; + + @Column + @Comment("学历") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String education; + + @Column + @Comment("岗位") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String post; + + @Column + @Comment("人事编制") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String personnelType; + + @Column + @Comment("出生年份") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String birthYear; + + @Column + @Comment("户籍") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String householdCity; + + @Column + @Comment("家乡") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String hometownCity; + + @Column + @Comment("身高厘米") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String heightCm; + + @Column + @Comment("体重千克") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String weightKg; + + @Column + @Comment("年收入万") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String annualIncome; + + @Column + @Comment("车房") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String carHouse; + + @Column + @Comment("自我描述") + @ColDefine(type = ColType.TEXT) + private String selfDescription; + + @Column + @Comment("家庭情况") + @ColDefine(type = ColType.TEXT) + private String familyInfo; + + @Column + @Comment("兴趣爱好") + @ColDefine(type = ColType.TEXT) + private String hobbies; + + @Column + @Comment("择偶意向") + @ColDefine(type = ColType.TEXT) + private String matePreference; + + @Column + @Comment("生活照URL") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String photoUrl; +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/service/ActivityFriendshipSignupService.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/service/ActivityFriendshipSignupService.java new file mode 100644 index 0000000..4fb909f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/service/ActivityFriendshipSignupService.java @@ -0,0 +1,53 @@ +package com.budwk.app.zhgh.activity.friendship.service; + +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.result.Result; +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.activity.friendship.models.ActivityFriendshipSignup; +import com.budwk.app.zhgh.activity.friendship.vo.ActivityFriendshipSignupVO; +import org.nutz.mvc.upload.TempFile; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +public interface ActivityFriendshipSignupService extends BaseService { + + /** + * 查询报名台账分页。 + * 参数 pageForm 为分页和人员信息关键字参数,仅支持工号、姓名模糊查询; + * sex、maritalStatus、education、post、personnelType、unionId、unitId 为页面下拉筛选值,分别对应性别、婚况、学历、岗位、人事编制、所属工会、所属单位; + * 返回值为 ActivityFriendshipSignupVO 分页数据,包含台账字段和关联用户的姓名、工会、单位信息。 + */ + Pagination pageData(PageForm pageForm, String sex, String maritalStatus, String education, String post, String personnelType, String unionId, String unitId); + + /** + * 查询移动端可查看的异性资料卡。 + * 根据当前登录用户性别自动筛选异性报名信息;返回值为资料卡展示 VO 列表。 + */ + List listMobileCards(); + + /** + * 新增报名记录。 + * 参数 signup 为前端表单数据;当编号为空时根据报名时间年份生成年份加三位流水号。 + */ + void insertSignup(ActivityFriendshipSignup signup); + + /** + * 修改报名记录。 + * 参数 signup 为前端表单数据;已有编号不变,编号为空时按报名时间补生成。 + */ + void updateSignup(ActivityFriendshipSignup signup); + + /** + * 导入报名 Excel。 + * 参数 file 为 xls/xlsx 文件,isFlag 为 true 时先清空台账再导入;返回导入统计和错误行。 + */ + Result doImport(TempFile file, Boolean isFlag); + + /** + * 导出报名台账。 + * 参数 response 为下载响应,导出字段与报名模板保持一致。 + */ + void exportExcel(HttpServletResponse response); +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/service/impl/ActivityFriendshipSignupServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/service/impl/ActivityFriendshipSignupServiceImpl.java new file mode 100644 index 0000000..27de480 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/service/impl/ActivityFriendshipSignupServiceImpl.java @@ -0,0 +1,412 @@ +package com.budwk.app.zhgh.activity.friendship.service.impl; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.ExcelImportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.ImportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.result.Result; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.sys.enums.SysFileEngineTypeEnum; +import com.budwk.app.sys.views.View_user; +import com.budwk.app.sys.services.SysFileService; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.activity.friendship.mode.ActivityFriendshipExcelMode; +import com.budwk.app.zhgh.activity.friendship.mode.ActivityFriendshipImportError; +import com.budwk.app.zhgh.activity.friendship.mode.ActivityFriendshipImportResult; +import com.budwk.app.zhgh.activity.friendship.models.ActivityFriendshipSignup; +import com.budwk.app.zhgh.activity.friendship.service.ActivityFriendshipSignupService; +import com.budwk.app.zhgh.activity.friendship.vo.ActivityFriendshipSignupVO; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +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.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.mvc.upload.TempFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.FileInputStream; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@IocBean(args = {"refer:dao"}) +public class ActivityFriendshipSignupServiceImpl extends BaseServiceImpl implements ActivityFriendshipSignupService { + + private static final int PHOTO_COLUMN_INDEX = 18; + + @Inject + private SysFileService sysFileService; + + public ActivityFriendshipSignupServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination pageData(PageForm pageForm, String sex, String maritalStatus, String education, String post, String personnelType, String unionId, String unitId) { + Sql sql = Sqls.create(""" + SELECT + afs.id, + afs.signupNo, + afs.loginName, + vu.username, + vu.unionId, + vu.unionName, + vu.unitId, + vu.unitName, + afs.signupTime, + afs.sex, + afs.maritalStatus, + afs.education, + afs.post, + afs.personnelType, + afs.birthYear, + afs.householdCity, + afs.hometownCity, + afs.heightCm, + afs.weightKg, + afs.annualIncome, + afs.carHouse, + afs.selfDescription, + afs.familyInfo, + afs.hobbies, + afs.matePreference, + afs.photoUrl, + afs.createdAt + FROM + activity_friendship_signup afs + LEFT JOIN vw_user vu ON vu.loginname = afs.loginName + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("afs.delFlag", "=", false); + cnd.andEX("afs.sex", "=", sex); + cnd.andEX("afs.maritalStatus", "=", maritalStatus); + cnd.andEX("afs.education", "=", education); + cnd.andEX("afs.post", "=", post); + cnd.andEX("afs.personnelType", "=", personnelType); + cnd.andEX("vu.unionId", "=", unionId); + cnd.andEX("vu.unitId", "=", unitId); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword()) && StrUtil.isNotBlank(pageForm.getSearchName())) { + cnd.and(Cnd.likeEX(getSearchColumn(pageForm.getSearchName()), pageForm.getSearchKeyword())); + } + cnd.desc("afs.signupNo"); + cnd.desc("afs.createdAt"); + sql.setCondition(cnd); + return listPageVO(pageForm, sql, ActivityFriendshipSignupVO.class); + } + + @Override + public void insertSignup(ActivityFriendshipSignup signup) { + fillSignupNoIfBlank(signup); + insert(signup); + } + + @Override + public void updateSignup(ActivityFriendshipSignup signup) { + fillSignupNoIfBlank(signup); + updateIgnoreNull(signup); + } + + @Override + public List listMobileCards() { + View_user currentUser = dao().fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId())); + if (currentUser == null || !List.of("男", "女").contains(currentUser.getSex())) { + return new ArrayList<>(); + } + String targetSex = "男".equals(currentUser.getSex()) ? "女" : "男"; + Sql sql = Sqls.create(""" + SELECT + afs.id, + afs.signupNo, + afs.loginName, + vu.username, + vu.unionId, + vu.unionName, + vu.unitId, + vu.unitName, + afs.signupTime, + afs.sex, + afs.maritalStatus, + afs.education, + afs.post, + afs.personnelType, + afs.birthYear, + afs.householdCity, + afs.hometownCity, + afs.heightCm, + afs.weightKg, + afs.annualIncome, + afs.carHouse, + afs.selfDescription, + afs.familyInfo, + afs.hobbies, + afs.matePreference, + afs.createdAt + FROM + activity_friendship_signup afs + LEFT JOIN vw_user vu ON vu.loginname = afs.loginName + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("afs.delFlag", "=", false); +// cnd.and("afs.sex", "=", targetSex); + cnd.desc("afs.signupNo"); + cnd.desc("afs.createdAt"); + sql.setCondition(cnd); + return listVO(sql, ActivityFriendshipSignupVO.class); + } + + @Override + public Result doImport(TempFile file, Boolean isFlag) { + if (Lang.isEmpty(file)) { + return Result.error("上传的文件不能为空"); + } + String extName = FileUtil.extName(file.getFile()); + if (!List.of("xls", "xlsx").contains(extName.toLowerCase())) { + return Result.error("请上传xls,xlsx文件"); + } + + ActivityFriendshipImportResult result = new ActivityFriendshipImportResult(); + List signups = new ArrayList<>(); + try (FileInputStream inputStream = new FileInputStream(file.getFile()); + Workbook workbook = WorkbookFactory.create(inputStream)) { + org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0); + Map rowPhotoUrlMap = buildRowPhotoUrlMap(sheet); + + // 导入参数说明:Excel 第 1 行为字段表头,返回值为按 @Excel 表头映射好的报名导入实体列表。 + ImportParams importParams = new ImportParams(); + importParams.setTitleRows(0); + importParams.setHeadRows(1); + List excelList = ExcelImportUtil.importExcel(file.getFile(), ActivityFriendshipExcelMode.class, importParams); + for (int dataIndex = 0; dataIndex < excelList.size(); dataIndex++) { + ActivityFriendshipSignup signup = BeanUtil.copyProperties(excelList.get(dataIndex), ActivityFriendshipSignup.class); + if (isBlankSignup(signup)) { + continue; + } + result.setTotalCount(result.getTotalCount() + 1); + signup.setPhotoUrl(rowPhotoUrlMap.get(dataIndex + importParams.getHeadRows() + importParams.getTitleRows())); + if (StrUtil.isBlank(signup.getLoginName())) { + result.getErrorList().add(new ActivityFriendshipImportError(dataIndex + importParams.getHeadRows() + importParams.getTitleRows() + 1, "", "工号不能为空")); + continue; + } + signups.add(signup); + } + } catch (Exception e) { + log.error("单身联谊报名导入失败", e); + return Result.error("读取不到数据,请检查excel文件格式"); + } + + if (Boolean.TRUE.equals(isFlag)) { + clear(Cnd.where("delFlag", "=", false)); + } + fillSignupNo(signups, Boolean.TRUE.equals(isFlag)); + if (Lang.isNotEmpty(signups)) { + insert(signups); + } + + result.setErrorCount(result.getErrorList().size()); + result.setSuccessCount(Math.max(result.getTotalCount() - result.getErrorCount(), 0)); + if (result.getErrorCount() > 0) { + return Result.success(result); + } + return Result.success("导入成功"); + } + + @Override + public void exportExcel(HttpServletResponse response) { + try { + Sql sql = Sqls.create(""" + SELECT + afs.signupNo, + afs.loginName, + vu.username, + vu.unionName, + vu.unitName, + afs.signupTime, + afs.sex, + afs.maritalStatus, + afs.education, + afs.post, + afs.personnelType, + afs.birthYear, + afs.householdCity, + afs.hometownCity, + afs.heightCm, + afs.weightKg, + afs.annualIncome, + afs.carHouse, + afs.selfDescription, + afs.familyInfo, + afs.hobbies, + afs.matePreference, + afs.photoUrl + FROM + activity_friendship_signup afs + LEFT JOIN vw_user vu ON vu.loginname = afs.loginName + WHERE + afs.delFlag = 0 + ORDER BY + afs.signupNo DESC, + afs.createdAt DESC + """); + List list = listVO(sql, ActivityFriendshipSignupVO.class); + List exportList = list.stream().map(item -> BeanUtil.copyProperties(item, ActivityFriendshipExcelMode.class)).collect(Collectors.toList()); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + exportParams.setTitle("单身联谊报名人员名单"); + exportParams.setSheetName("报名人员名单"); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ActivityFriendshipExcelMode.class, exportList); + CommonDownloadUtil.download("单身联谊报名人员名单.xlsx", workbook, response); + } catch (Exception e) { + log.error("单身联谊报名导出失败", e); + } + } + + /** + * 将前端搜索字段转换为连表查询字段。 + * 参数 searchName 为前端选择的搜索项;返回值为带表别名的数据库字段,避免连表后字段歧义。 + */ + private String getSearchColumn(String searchName) { + return switch (searchName) { + case "loginName" -> "afs.loginName"; + case "username" -> "vu.username"; + default -> "afs.loginName"; + }; + } + + /** + * 解析 Excel 内嵌生活照。 + * 参数 sheet 为报名工作表;返回值 key 为 POI 行号,value 为系统文件下载 URL。 + */ + private Map buildRowPhotoUrlMap(org.apache.poi.ss.usermodel.Sheet sheet) { + Map rowPhotoUrlMap = new HashMap<>(); + if (!(sheet instanceof XSSFSheet xssfSheet)) { + return rowPhotoUrlMap; + } + for (POIXMLDocumentPart relation : xssfSheet.getRelations()) { + if (!(relation instanceof XSSFDrawing drawing)) { + continue; + } + for (XSSFShape shape : drawing.getShapes()) { + if (!(shape instanceof XSSFPicture picture)) { + continue; + } + XSSFClientAnchor anchor = picture.getClientAnchor(); + if (anchor == null || anchor.getCol1() != PHOTO_COLUMN_INDEX) { + continue; + } + byte[] imageBytes = picture.getPictureData().getData(); + String extension = picture.getPictureData().suggestFileExtension(); + String fileName = "friendship-photo-" + (anchor.getRow1() + 1) + "." + (StrUtil.isBlank(extension) ? "png" : extension); + String photoUrl = sysFileService.uploadBytesReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), fileName, imageBytes); + rowPhotoUrlMap.put(anchor.getRow1(), photoUrl); + } + } + return rowPhotoUrlMap; + } + + /** + * 判断导入实体是否为空行。 + * 参数 signup 为 EasyPOI 根据表头映射出的导入对象;返回 true 表示所有业务文本字段均为空,不参与统计和入库。 + */ + private boolean isBlankSignup(ActivityFriendshipSignup signup) { + return signup == null + || StrUtil.isAllBlank(signup.getLoginName(), signup.getSignupTime(), signup.getSex(), signup.getMaritalStatus(), + signup.getEducation(), signup.getPost(), signup.getPersonnelType(), signup.getBirthYear(), signup.getHouseholdCity(), + signup.getHometownCity(), signup.getHeightCm(), signup.getWeightKg(), signup.getAnnualIncome(), signup.getCarHouse(), + signup.getSelfDescription(), signup.getFamilyInfo(), signup.getHobbies(), signup.getMatePreference(), signup.getPhotoUrl()); + } + + /** + * 批量生成报名编号。 + * 参数 signups 为待入库报名记录,resetFromOne 表示是否忽略库中已有编号;编号格式为报名年份加三位流水号。 + */ + private void fillSignupNo(List signups, boolean resetFromOne) { + Map yearMaxNoMap = new HashMap<>(); + signups.sort(Comparator.comparing(ActivityFriendshipSignup::getSignupTime, Comparator.nullsLast(String::compareTo))); + for (ActivityFriendshipSignup signup : signups) { + String year = parseSignupYear(signup.getSignupTime()); + int currentMax = yearMaxNoMap.computeIfAbsent(year, key -> resetFromOne ? 0 : queryMaxSignupNo(key)); + int nextNo = currentMax + 1; + signup.setSignupNo(year + String.format("%03d", nextNo)); + yearMaxNoMap.put(year, nextNo); + } + } + + /** + * 单条补充报名编号。 + * 参数 signup 为表单报名记录;当编号为空时按报名时间年份和数据库最大流水号生成下一号。 + */ + private void fillSignupNoIfBlank(ActivityFriendshipSignup signup) { + if (signup == null || StrUtil.isNotBlank(signup.getSignupNo())) { + return; + } + String year = parseSignupYear(signup.getSignupTime()); + signup.setSignupNo(year + String.format("%03d", queryMaxSignupNo(year) + 1)); + } + + /** + * 查询指定年份现有最大流水号。 + * 参数 year 为四位年份;返回值为该年份后三位流水号最大值,没有记录时返回 0。 + */ + private int queryMaxSignupNo(String year) { + List list = query(Cnd.where("delFlag", "=", false) + .and("signupNo", "like", year + "%") + .desc("signupNo")); + if (Lang.isEmpty(list) || StrUtil.isBlank(list.get(0).getSignupNo())) { + return 0; + } + String signupNo = list.get(0).getSignupNo(); + if (signupNo.length() <= 4) { + return 0; + } + try { + return Integer.parseInt(signupNo.substring(4)); + } catch (Exception e) { + return 0; + } + } + + /** + * 解析报名时间年份。 + * 参数 signupTime 支持完整日期时间或直接填写年份;无法解析时使用当前年份,避免导入中断。 + */ + private String parseSignupYear(String signupTime) { + if (StrUtil.isBlank(signupTime)) { + return String.valueOf(LocalDate.now().getYear()); + } + String value = signupTime.trim(); + if (value.length() >= 4 && value.substring(0, 4).matches("\\d{4}")) { + return value.substring(0, 4); + } + try { + return String.valueOf(LocalDate.parse(value, DateTimeFormatter.ISO_LOCAL_DATE).getYear()); + } catch (Exception e) { + return String.valueOf(LocalDate.now().getYear()); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/friendship/vo/ActivityFriendshipSignupVO.java b/src/main/java/com/budwk/app/zhgh/activity/friendship/vo/ActivityFriendshipSignupVO.java new file mode 100644 index 0000000..463e21f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/friendship/vo/ActivityFriendshipSignupVO.java @@ -0,0 +1,38 @@ +package com.budwk.app.zhgh.activity.friendship.vo; + +import lombok.Data; + +/** + * 单身联谊报名台账前端展示数据。 + */ +@Data +public class ActivityFriendshipSignupVO { + + private String id; + private String signupNo; + private String loginName; + private String username; + private String unionId; + private String unionName; + private String unitId; + private String unitName; + private String signupTime; + private String sex; + private String maritalStatus; + private String education; + private String post; + private String personnelType; + private String birthYear; + private String householdCity; + private String hometownCity; + private String heightCm; + private String weightKg; + private String annualIncome; + private String carHouse; + private String selfDescription; + private String familyInfo; + private String hobbies; + private String matePreference; + private String photoUrl; + private Long createdAt; +} diff --git a/src/main/resources/views/platform/zhgh/activity/friendship/index.html b/src/main/resources/views/platform/zhgh/activity/friendship/index.html new file mode 100644 index 0000000..413607a --- /dev/null +++ b/src/main/resources/views/platform/zhgh/activity/friendship/index.html @@ -0,0 +1,471 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + 提交 + + + + +
+ + + + + + + 清空更新 + 追加 + + + + 选择文件 +
只能上传 xls/xlsx 文件,生活照需为 Excel 内嵌图片
+
+
+
+
+ + +

总记录数:{{ importResult.totalCount }}

+

成功数:{{ importResult.successCount }}

+

错误数:{{ importResult.errorCount }}

+ + + + + +
+
+
+
+ 取消 + 确定 +
+
+
+
+ + + + diff --git a/src/main/resources/views/platform/zhghh5/activity/friendship/index.html b/src/main/resources/views/platform/zhghh5/activity/friendship/index.html new file mode 100644 index 0000000..7f007f0 --- /dev/null +++ b/src/main/resources/views/platform/zhghh5/activity/friendship/index.html @@ -0,0 +1,803 @@ + + +
+
+
+ + + +
+
+ + + + + +
+
+ +
{{ currentIndex + 1 }}/{{ cardList.length }}
+
+ +
+ + + + +
+
+ +
+ {{ item.sex === '男' ? '👨' : '👩' }} + {{ item.sex === '男' ? '男士' : '女士' }} +
+
+ +
+ +
+

{{ item.birthYear || '未知年份' }}年出生

+

{{ buildSubtitle(item) }}

+
+ +
+
+ + 身高 + {{ item.heightCm || '未知' }}cm +
+
+ + 体重 + {{ item.weightKg || '未知' }}kg +
+
+ $ + 年收入 + {{ item.annualIncome || '未知' }}万 +
+
+ + 房车 + {{ item.carHouse || '未知' }} +
+
+ + 户籍 + {{ item.householdCity || '未知' }} +
+
+ + 家乡 + {{ item.hometownCity || '未知' }} +
+
+ +
+
+

✍ 自我描述

+

{{ item.selfDescription || '暂无' }}

+
+
+

👨‍👩‍👧‍👦 家庭情况

+

{{ item.familyInfo || '暂无' }}

+
+
+

🎯 兴趣爱好

+

{{ item.hobbies || '暂无' }}

+
+
+

💬 择偶意向

+

{{ item.matePreference || '暂无' }}

+
+
+
+
+
+
+ +
+
🛡 隐私保护:信息已脱敏处理
+

若有意向,请联系校工会

+
+ + + + +
+
+ + + + + +