This commit is contained in:
=
2026-05-06 19:42:49 +08:00
29 changed files with 1076 additions and 210 deletions
@@ -112,7 +112,7 @@ public class ActivityCultureAuditActivityController {
cnd.andEX("tissue.unionId", "=", unionId);
cnd.andEX("tissue.activity_type", "=", activity_type);
if (approval) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
@@ -9,6 +9,7 @@ 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.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.SysMsgService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -81,17 +82,20 @@ public class ActivityWorksCollectionManageController {
@At
@SaCheckPermission("activity.workscollection.manage")
@Aop(TransAop.READ_COMMITTED)
public Result insert(@Param("data") @Valid Activity_works_collection worksCollection) {
worksCollection.setIsSubmit(true);
dao.insertWith(worksCollection, "subjectTypes");
worksCollection.getSubjectTypes().forEach(item -> {
dao.insertLinks(item, "worksTypes");
});
syncHomeActivity(worksCollection);
return Result.success();
}
@At
@SaCheckPermission("activity.workscollection.manage")
@Aop(TransAop.READ_COMMITTED)
public Result save(@Param("data") @Valid Activity_works_collection worksCollection) {
worksCollection.setIsSubmit(false);
dao.insertWith(worksCollection, "subjectTypes");
@@ -122,6 +126,7 @@ public class ActivityWorksCollectionManageController {
Cnd.where(Activity_works_worksType::getId, "not in ", workIds)
.and(Activity_works_worksType::getSubjectId,"=",item.getId()));
});
syncHomeActivity(worksCollection);
return Result.success();
}
@@ -132,6 +137,7 @@ public class ActivityWorksCollectionManageController {
dao.delete(Activity_works_collection.class, id);
dao.clear(Activity_works_collection_upload.class, Cnd.where(Activity_works_collection_upload::getActivityId, "=", id));
dao.clear(Activity_works_subjectType.class, Cnd.where(Activity_works_subjectType::getActivityId, "=", id));
dao.delete(Sys_home_activity.class, id);
return Result.success();
}
@@ -148,11 +154,26 @@ public class ActivityWorksCollectionManageController {
@At
@SaCheckPermission("activity.workscollection.manage")
@Aop(TransAop.READ_COMMITTED)
public Result enableChange(@Valid String id, @Valid Boolean enable) {
dao.update(Activity_works_collection.class, Chain.make("enable", enable), Cnd.where("id", "=", id));
Activity_works_collection worksCollection = dao.fetch(Activity_works_collection.class, id);
worksCollection.setEnable(enable);
syncHomeActivity(worksCollection);
return Result.success();
}
/**
* 同步作品征集到首页活动表:开启的活动写入首页,关闭的活动移除首页入口。
*/
private void syncHomeActivity(Activity_works_collection worksCollection) {
if (Boolean.TRUE.equals(worksCollection.getEnable())) {
dao.insertOrUpdate(worksCollection.covertToSysHomeActivity());
} else {
dao.delete(Sys_home_activity.class, worksCollection.getId());
}
}
@At
@SaCheckPermission("activity.workscollection.manage")
public Result sendNotice(@Valid String id) {
@@ -11,6 +11,7 @@ import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection;
import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection_upload;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
@@ -18,6 +19,7 @@ import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.dao.util.cri.SqlExpressionGroup;
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.At;
@@ -94,11 +96,17 @@ public class ActivityWorksCollectionUploadController {
*/
@At
@SaCheckPermission("activity.workscollection.upload")
@Aop(TransAop.READ_COMMITTED)
public Result insert(@Param("data") @Valid Activity_works_collection_upload upload) {
String activityId = upload.getActivityId();
Activity_works_collection activity = dao.fetch(Activity_works_collection.class, activityId);
if (activity.getEndDateTime().getTime() < System.currentTimeMillis()){
long now = System.currentTimeMillis();
// 作品上传只允许在活动开始时间和结束时间之间提交,防止活动未开始或已结束后继续上传。
if (activity.getStartDateTime().getTime() > now) {
return Result.error("活动未开始,不能上传!");
}
if (activity.getEndDateTime().getTime() < now){
return Result.error("活动已结束!");
}
if (activity.getActivityGroupId() != null) {
@@ -124,9 +132,15 @@ public class ActivityWorksCollectionUploadController {
@At
@SaCheckPermission("activity.workscollection.upload")
@Aop(TransAop.READ_COMMITTED)
public Result update(@Param("data") @Valid Activity_works_collection_upload upload) {
Activity_works_collection activity = dao.fetch(Activity_works_collection.class, upload.getActivityId());
if (activity.getEndDateTime().getTime() < System.currentTimeMillis()){
long now = System.currentTimeMillis();
// 编辑作品同样按活动时间窗口控制,避免过期或未开始活动被继续提交作品信息。
if (activity.getStartDateTime().getTime() > now) {
return Result.error("活动未开始,不能上传!");
}
if (activity.getEndDateTime().getTime() < now){
return Result.error("活动已结束!");
}
dao.updateIgnoreNull(upload);
@@ -155,12 +169,19 @@ public class ActivityWorksCollectionUploadController {
@At
@SaCheckPermission("activity.workscollection.upload")
/**
* 查询当前登录人有权限上传作品的活动主题。
*
* @param year 活动年度,前端传年度选择框的 yyyy 值,按活动开始时间所在年份过滤。
* @return Resultdata 为活动列表,仅返回 id、name,且保留已开启和面向对象权限过滤。
*/
@ApiOperation("获取有权限的活动列表")
@Ok("json:{ignoreNull:true}")
public Result listPerMissionActivity() {
public Result listPerMissionActivity(Long year) {
Dao extDao = Daos.ext(dao, FieldFilter.create(Activity_works_collection.class, "id|name|"));
Cnd cnd = Cnd.NEW();
cnd.and(Activity_works_collection::getEnable,"=",1);
cnd.andEX("YEAR(startDateTime)", "=", year);
cnd.desc(Activity_works_collection::getStartDateTime);
List<Activity_works_collection> list = extDao.query(Activity_works_collection.class, cnd);
list = list.stream().filter(item -> {
@@ -1,6 +1,8 @@
package com.budwk.app.zhgh.activity.workscollection.models;
import com.budwk.app.base.model.BaseModel;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.services.SysHomeConvert;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
@@ -17,7 +19,7 @@ import java.util.List;
@Table("activity_works_collection")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("作品征集活动")
public class Activity_works_collection extends BaseModel {
public class Activity_works_collection extends BaseModel implements SysHomeConvert {
@Name
@Comment("ID")
@@ -123,4 +125,21 @@ public class Activity_works_collection extends BaseModel {
@Many(field = "activityId")
private List<Activity_works_collection_upload> uploads;
@Override
public Sys_home_activity covertToSysHomeActivity() {
Sys_home_activity sysHomeActivity = new Sys_home_activity();
sysHomeActivity.setId(this.getId());
sysHomeActivity.setName(this.getName());
sysHomeActivity.setCover(this.getCover());
sysHomeActivity.setContent(this.getContent());
sysHomeActivity.setUrl("/platform/activity/worksCollection/upload");
sysHomeActivity.setH5Url("/platform/activity/worksCollection/upload/h5");
sysHomeActivity.setStartDate(this.getStartDateTime());
sysHomeActivity.setEndDate(this.getEndDateTime());
sysHomeActivity.setAllowUserGroupId(this.getActivityGroupId());
sysHomeActivity.setEnable(Boolean.TRUE.equals(this.getEnable()));
sysHomeActivity.setClassPath(this.getClass().getPackageName() + this.getClass().getName());
return sysHomeActivity;
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.message.strategy.impl;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
import com.budwk.app.zhgh.dayofficework.message.strategy.GlobalMessageSendStrategy;
@@ -21,6 +22,8 @@ public class SmsMessageSendStrategy implements GlobalMessageSendStrategy {
@Inject
private Dao dao;
@Inject
private SmsService smsService;
@Override
public GlobalMessageChannel getChannel() {
@@ -70,29 +73,24 @@ public class SmsMessageSendStrategy implements GlobalMessageSendStrategy {
}
/**
* 发送短信消息
* 发送短信消息
* 接收人由全局消息服务传入的用户ID或登录名查询得到,发送时使用用户工号对接通讯平台;
* 返回结果由 SmsService 内部解析并记录,当前策略只负责逐个用户触发发送并记录异常。
*/
private void sendSmsMessage(String title, String content, Integer type, List<Sys_user> users, JSONObject config) {
for (Sys_user user : users) {
try {
String mobile = user.getMobile();
if (mobile == null || mobile.trim().isEmpty()) {
log.warn("用户{}没有手机号,跳过短信发送", user.getUsername());
String loginName = user.getLoginname();
if (loginName == null || loginName.trim().isEmpty()) {
log.warn("用户{}没有号,跳过短信发送", user.getUsername());
continue;
}
// 构建短信内容
String smsContent = buildSmsContent(title, content, type);
// 执行发送
boolean success = true;
if (success) {
log.info("短信发送成功:用户{},手机号:{}", user.getUsername(), mobile);
} else {
log.warn("短信发送失败:用户{},手机号:{}", user.getUsername(), mobile);
}
smsService.sendSmsByAccount(loginName, smsContent);
log.info("短信发送完成:用户{},工号:{}", user.getUsername(), loginName);
} catch (Exception e) {
log.error("发送短信给用户{}时出错:{}", user.getUsername(), e.getMessage(), e);
}
@@ -0,0 +1,113 @@
package com.budwk.app.zhgh.dayofficework.message.strategy.impl;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
import com.budwk.app.zhgh.dayofficework.message.strategy.GlobalMessageSendStrategy;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.List;
/**
* 微信发送策略
*/
@IocBean
@Slf4j
public class WechatMessageSendStrategy implements GlobalMessageSendStrategy {
@Inject
private Dao dao;
@Inject
private SmsService smsService;
@Override
public GlobalMessageChannel getChannel() {
return GlobalMessageChannel.WECHAT;
}
@Override
public boolean supportAsync() {
return true;
}
@Override
public void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
try {
log.info("开始发送微信消息,标题:{},接收人数量:{}", title, receiverIds.size());
// receiverIds 为 sys_user.id,按用户ID查询后使用工号对接通讯平台微信文本消息接口。
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getId, "in", receiverIds));
sendWechatMessage(title, content, type, users, config);
log.info("微信消息发送完成,标题:{},接收人数量:{}", title, users.size());
} catch (Exception e) {
log.error("微信消息发送失败,标题:{},错误:{}", title, e.getMessage(), e);
throw new RuntimeException("微信消息发送失败:" + e.getMessage(), e);
}
}
@Override
public void sendByLoginName(String title, String content, Integer type, List<String> receiverLoginNames, JSONObject config) {
try {
log.info("开始发送微信消息(按登录名),标题:{},接收人数量:{}", title, receiverLoginNames.size());
// receiverLoginNames 为 sys_user.loginname,查询用户后仍使用工号作为通讯平台接收人标识。
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getLoginname, "in", receiverLoginNames));
sendWechatMessage(title, content, type, users, config);
log.info("微信消息发送完成(按登录名),标题:{},接收人数量:{}", title, users.size());
} catch (Exception e) {
log.error("微信消息发送失败(按登录名),标题:{},错误:{}", title, e.getMessage(), e);
throw new RuntimeException("微信消息发送失败:" + e.getMessage(), e);
}
}
/**
* 发送微信文本消息。
* 接收人来自全局消息服务传入的用户ID或登录名,方法内部转换为 Sys_user 后取工号发送;
* SmsService 返回 MsgPlatformResponse,当前策略按单人发送记录日志,单个用户失败不影响其他接收人。
*/
private void sendWechatMessage(String title, String content, Integer type, List<Sys_user> users, JSONObject config) {
for (Sys_user user : users) {
try {
String loginName = user.getLoginname();
if (loginName == null || loginName.trim().isEmpty()) {
log.warn("用户{}没有工号,跳过微信发送", user.getUsername());
continue;
}
String wechatContent = buildWechatContent(title, content, type);
smsService.sendWechatTextByAccountOrMobile(loginName, wechatContent);
log.info("微信发送完成:用户{},工号:{}", user.getUsername(), loginName);
} catch (Exception e) {
log.error("发送微信给用户{}时出错:{}", user.getUsername(), e.getMessage(), e);
}
}
}
/**
* 构建微信文本内容。
* type=2 表示待办消息,其余类型按系统通知处理,内容格式保持与短信策略一致。
*/
private String buildWechatContent(String title, String content, Integer type) {
StringBuilder sb = new StringBuilder();
if (type != null && type == 2) {
sb.append("【待办通知】");
} else {
sb.append("【系统通知】");
}
sb.append(title);
if (content != null && !content.trim().isEmpty()) {
sb.append("").append(content);
}
return sb.toString();
}
}
@@ -120,6 +120,7 @@ public class ProposalSecondedController {
Cnd cnd = Cnd.NEW();
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
cnd.and("info.createUserId", "!=", SecurityUtil.getUserId());
if (approval) {
// 已附议页签只展示当前登录附议人已经处理过的记录。
@@ -179,7 +179,7 @@ public class TeacherCongressDelegatePushController {
""");
sql.setParam("sessionId", sessionId);
Cnd cnd = Cnd.NEW();
// cnd.and("u.member", "=", 1);
cnd.and("u.member", "=", 1);
cnd.and("u.username", "is not", null);
cnd.andEX("u.id", "not in", userIds);
cnd.and("tcd.userId", "is", null);
@@ -214,7 +214,7 @@ public class WelfareProject extends BaseModel implements SysHomeConvert {
sysHomeActivity.setH5Url("/platform/h5/welfare/userSelect/index?id=" + this.getId());
sysHomeActivity.setStartDate(this.getChoiceTimeStart());
sysHomeActivity.setEndDate(this.getChoiceTimeEnd());
sysHomeActivity.setAllowUserSql("select userId from welfare_list where projectId = '" + this.getId() + "' and userId = @userId");
sysHomeActivity.setAllowUserSql("select userId from welfare_list where projectId = (select id from welfare_project where id='" + this.getId() + "' and provideMode!=1) and userId = @userId ");
sysHomeActivity.setEnable(true);
sysHomeActivity.setClassPath(this.getClass().getPackageName() + this.getClass().getName());
return sysHomeActivity;