commit
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.budwk.app.base.dao;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
/**
|
||||
* 条件构造辅助类。
|
||||
* 当前健步走移植代码需要按页面查询条件动态追加字段查询和排序,空字段不应参与 SQL 条件。
|
||||
*/
|
||||
public class CndPlus extends Cnd {
|
||||
private static final NutMap ORDER_MAP = NutMap.NEW().addv("ascending", "asc").addv("descending", "desc");
|
||||
|
||||
public static CndPlus create() {
|
||||
return new CndPlus();
|
||||
}
|
||||
|
||||
public CndPlus and(PageForm pageForm) {
|
||||
if (pageForm != null && StrUtil.isAllNotBlank(pageForm.getSearchName(), pageForm.getSearchKeyword())) {
|
||||
this.where().andLike(pageForm.getSearchName(), pageForm.getSearchKeyword());
|
||||
}
|
||||
if (pageForm != null && StrUtil.isAllNotBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
|
||||
this.orderBy(pageForm.getPageOrderName(), ORDER_MAP.getString(pageForm.getPageOrderBy()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅在字段名和值都有效时追加条件,避免可选筛选项为空时生成无效 SQL。
|
||||
*
|
||||
* @param name 字段名或表达式
|
||||
* @param op 比较操作符
|
||||
* @param value 比较值
|
||||
* @return 当前条件对象
|
||||
*/
|
||||
public CndPlus andEX(String name, String op, Object value) {
|
||||
if (StrUtil.isNotBlank(name) && !Lang.isEmpty(value)) {
|
||||
this.and(name, op, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package com.budwk.app.task.job.fitnesswalk;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.budwk.app.sys.models.Sys_task;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.impl.FitnessWalkCommonServiceImpl;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author zhf
|
||||
* @date 2026/4/11 14:01
|
||||
* @description 自动创建活动
|
||||
*/
|
||||
@IocBean
|
||||
@Slf4j
|
||||
public class FitnessWalkAddActivityJob implements Job {
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
Ioc ioc = Mvcs.ctx().getDefaultIoc();
|
||||
Dao dao = ioc.get(Dao.class);
|
||||
WeAppCloudUtil weAppCloudUtil = ioc.get(WeAppCloudUtil.class);
|
||||
// Quartz 任务上下文里按接口取 Bean 容易命中 Nutz 的接口实例化异常,这里统一按实现类获取后再走 service 公共方法。
|
||||
FitnessWalkCommonService fitnessWalkCommonService = ioc.get(FitnessWalkCommonServiceImpl.class);
|
||||
|
||||
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
|
||||
String sourceActivityId = getJobString(jobDataMap, "sourceActivityId");
|
||||
String deleteId = getJobString(jobDataMap, "deleteId");
|
||||
long targetDate = getJobLong(jobDataMap, "targetDate");
|
||||
|
||||
try {
|
||||
FitnessWalkActivity sourceActivity = loadSourceActivity(weAppCloudUtil, sourceActivityId);
|
||||
if (sourceActivity == null) {
|
||||
disableCurrentTask(dao, deleteId, sourceActivityId);
|
||||
return;
|
||||
}
|
||||
|
||||
FitnessWalkActivity newActivity = JSON.parseObject(JSON.toJSONString(sourceActivity), FitnessWalkActivity.class);
|
||||
shiftActivityDate(sourceActivity, newActivity, targetDate);
|
||||
resetBranchActivityNameByRelationProject(dao, newActivity, targetDate);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long normalizedTargetDate = DateUtil.beginOfDay(DateUtil.date(targetDate)).getTime();
|
||||
newActivity.set_id(null);
|
||||
newActivity.setCreatedAt(currentTime);
|
||||
newActivity.setUpdatedAt(currentTime);
|
||||
// 自动复制出的活动补充来源标记,便于后续编辑原活动时识别哪些日期已经真正生成过活动。
|
||||
newActivity.setAutoCreateSourceActivityId(sourceActivityId);
|
||||
newActivity.setAutoCreateTargetDate(normalizedTargetDate);
|
||||
|
||||
JSONObject insertResult = weAppCloudUtil.request(WeAppCloudUtil.CRUD.INSERT,
|
||||
"db.collection('activity').add({data:" + Json.toJson(newActivity) + "})");
|
||||
if (insertResult.containsKey("id_list") && insertResult.getJSONArray("id_list") != null && !insertResult.getJSONArray("id_list").isEmpty()) {
|
||||
newActivity.set_id(insertResult.getJSONArray("id_list").getString(0));
|
||||
}
|
||||
|
||||
// 自动复制出的活动也要同步刷新缓存,并补齐该活动自身的抽奖任务。
|
||||
fitnessWalkCommonService.addLotteryTask(newActivity);
|
||||
fitnessWalkCommonService.addOrEditDoSaveRedis(newActivity);
|
||||
} catch (Exception e) {
|
||||
log.error("自动连续创建活动失败, sourceActivityId={}", sourceActivityId, e);
|
||||
throw new JobExecutionException(e);
|
||||
} finally {
|
||||
disableCurrentTask(dao, deleteId, sourceActivityId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quartz 的 JobDataMap 可能直接存的是 Long、Integer 等对象,不能一律用 getString 读取。
|
||||
* 这里统一兜底转成字符串,避免任务参数类型变化时再次触发 ClassCastException。
|
||||
*/
|
||||
private String getJobString(JobDataMap jobDataMap, String key) {
|
||||
Object value = jobDataMap.get(key);
|
||||
return value == null ? null : String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务中的日期时间参数既可能是数字,也可能是字符串。
|
||||
* 这里统一兼容处理,确保 targetDate 在测试和正式任务下都能稳定转成时间戳。
|
||||
*/
|
||||
private long getJobLong(JobDataMap jobDataMap, String key) {
|
||||
Object value = jobDataMap.get(key);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("定时任务缺少参数:" + key);
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return Long.parseLong(String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据原活动ID查询原活动。
|
||||
* 定时任务执行时统一从云库读取最新活动内容,确保复制时拿到的是当前活动的最新配置。
|
||||
*/
|
||||
private FitnessWalkActivity loadSourceActivity(WeAppCloudUtil weAppCloudUtil, String sourceActivityId) {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY,
|
||||
"db.collection('activity').doc('" + sourceActivityId + "').get()");
|
||||
return jsonObject.getJSONArray("data").stream()
|
||||
.map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将活动的报名时间和活动时间整体平移到目标日期。
|
||||
* 这里保留原活动的时分秒和时长,仅根据目标日期替换所属自然日。
|
||||
*/
|
||||
private void shiftActivityDate(FitnessWalkActivity sourceActivity, FitnessWalkActivity newActivity, long targetDate) {
|
||||
DateTime sourceStartDate = DateUtil.beginOfDay(DateUtil.date(sourceActivity.getStartTime()));
|
||||
DateTime targetStartDate = DateUtil.beginOfDay(DateUtil.date(targetDate));
|
||||
long offsetMillis = targetStartDate.getTime() - sourceStartDate.getTime();
|
||||
|
||||
if (sourceActivity.getApplyTime() != null && sourceActivity.getApplyTime().length == 2) {
|
||||
Long[] applyTime = Arrays.copyOf(sourceActivity.getApplyTime(), 2);
|
||||
applyTime[0] = applyTime[0] + offsetMillis;
|
||||
applyTime[1] = applyTime[1] + offsetMillis;
|
||||
newActivity.setApplyTime(applyTime);
|
||||
newActivity.setApplyStartTime(applyTime[0]);
|
||||
newActivity.setApplyEndTime(applyTime[1]);
|
||||
}
|
||||
|
||||
if (sourceActivity.getTime() != null && sourceActivity.getTime().length == 2) {
|
||||
Long[] time = Arrays.copyOf(sourceActivity.getTime(), 2);
|
||||
time[0] = time[0] + offsetMillis;
|
||||
time[1] = time[1] + offsetMillis;
|
||||
newActivity.setTime(time);
|
||||
newActivity.setStartTime(time[0]);
|
||||
newActivity.setEndTime(time[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动连续创建活动时,分活动名称统一取“关联活动配置名称 + 日期前缀”。
|
||||
* 优先读取关联活动配置表中的主活动名称;若当前活动没有配置关联活动,则回退到原分活动名称。
|
||||
*/
|
||||
private void resetBranchActivityNameByRelationProject(Dao dao, FitnessWalkActivity newActivity, long targetDate) {
|
||||
if (newActivity == null) {
|
||||
return;
|
||||
}
|
||||
String datePrefix = DateUtil.format(DateUtil.date(targetDate), "yyyy年MM月dd日");
|
||||
String targetName = null;
|
||||
if (StrUtil.isNotBlank(newActivity.getRelationActivityId())) {
|
||||
FitnessWalkActivityRelationProject relationProject = dao.fetch(
|
||||
FitnessWalkActivityRelationProject.class,
|
||||
Cnd.where("id", "=", newActivity.getRelationActivityId())
|
||||
);
|
||||
if (relationProject != null && StrUtil.isNotBlank(relationProject.getMasterActivityName())) {
|
||||
targetName = relationProject.getMasterActivityName().trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(targetName) && StrUtil.isNotBlank(newActivity.getBranchActivityName())) {
|
||||
targetName = newActivity.getBranchActivityName().trim();
|
||||
}
|
||||
if (StrUtil.isBlank(targetName)) {
|
||||
return;
|
||||
}
|
||||
if (targetName.startsWith(datePrefix)) {
|
||||
newActivity.setBranchActivityName(targetName);
|
||||
return;
|
||||
}
|
||||
newActivity.setBranchActivityName(datePrefix + " " + targetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前任务执行完成后禁用自身,避免同一日期重复创建活动。
|
||||
*/
|
||||
private void disableCurrentTask(Dao dao, String deleteId, String sourceActivityId) {
|
||||
dao.update(Sys_task.class, Chain.make("disabled", 1),
|
||||
Cnd.where("data", "like", "%" + deleteId + "%").and("data", "like", "%" + sourceActivityId + "%"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package com.budwk.app.task.job.fitnesswalk;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.budwk.app.sys.models.Sys_task;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkRaffleUser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.Static;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@Slf4j
|
||||
public class FitnessWalkLotteryJob implements Job {
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
Ioc ioc = Mvcs.ctx().getDefaultIoc();
|
||||
Dao dao = ioc.get(Dao.class);
|
||||
|
||||
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
|
||||
String deleteId = jobDataMap.getString("deleteId");
|
||||
|
||||
int lotteryQualificationStep = Integer.parseInt(jobDataMap.getString("lotteryQualificationStep"));
|
||||
int lotteryUserNum = Integer.parseInt(jobDataMap.getString("lotteryUserNum"));
|
||||
String mode = jobDataMap.getString("mode");
|
||||
String activityId = jobDataMap.getString("id");
|
||||
String lotteryQualificationDay = jobDataMap.getString("lotteryQualificationDay");
|
||||
|
||||
String awardName;
|
||||
Date startDate = null;
|
||||
Date endDate = null;
|
||||
if ("custom".equals(mode)) {
|
||||
awardName = jobDataMap.getString("awardName");
|
||||
String startTimeStr = jobDataMap.getString("startDate");
|
||||
String endTimeStr = jobDataMap.getString("endDate");
|
||||
startDate = DateUtil.parse(startTimeStr, "yyyy-MM-dd");
|
||||
endDate = DateUtil.parse(endTimeStr, "yyyy-MM-dd");
|
||||
} else {
|
||||
awardName = null;
|
||||
}
|
||||
List<NutMap> userList = new ArrayList<>();
|
||||
|
||||
if ("custom".equals(mode)) {
|
||||
// 按当前项目 vw_user 视图查询用户及分工会、单位名称,保持抽奖记录字段一致。
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.id,
|
||||
COALESCE(SUM(sub.step), 0) AS total_steps,
|
||||
u.loginname,
|
||||
u.username,
|
||||
u.unionid,
|
||||
u.unionname,
|
||||
u.unitid,
|
||||
u.unitname,
|
||||
over3k.standardsDays
|
||||
FROM
|
||||
`vw_user` u
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
userId,
|
||||
applyDate,
|
||||
step
|
||||
FROM
|
||||
`fitness_walk_step`
|
||||
WHERE
|
||||
activityId = @activityId
|
||||
AND DATE(applyDate) >= @startDate
|
||||
AND DATE(applyDate) <= @endDate
|
||||
$lotteryQualificationDaySql
|
||||
GROUP BY
|
||||
userId,
|
||||
applyDate
|
||||
) AS sub ON u.id = sub.userId
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
userId,
|
||||
COUNT(DISTINCT DATE(applyDate)) AS standardsDays
|
||||
FROM
|
||||
`fitness_walk_step`
|
||||
WHERE
|
||||
activityId = @activityId
|
||||
AND DATE(applyDate) >= @startDate
|
||||
AND DATE(applyDate) <= @endDate
|
||||
$lotteryQualificationDaySql
|
||||
GROUP BY
|
||||
userId
|
||||
) AS over3k ON u.id = over3k.userId
|
||||
WHERE u.loginname NOT IN (
|
||||
SELECT loginName FROM fitness_walk_award_user WHERE activityId = @activityId
|
||||
)
|
||||
GROUP BY
|
||||
u.id,
|
||||
u.username,
|
||||
u.loginname,
|
||||
u.unionname,
|
||||
u.unitname
|
||||
HAVING
|
||||
$stepHavingSql
|
||||
""");
|
||||
if (Strings.isNotBlank(lotteryQualificationDay)) {
|
||||
sql.setVar("lotteryQualificationDaySql", new Static(" AND step >= '%s' ".formatted(lotteryQualificationStep)));
|
||||
sql.setVar("stepHavingSql", new Static(" over3k.standardsDays >= '%s' ".formatted(lotteryQualificationDay)));
|
||||
} else {
|
||||
sql.setVar("stepHavingSql", new Static(" COALESCE(SUM(sub.step), 0) >= '%s' ".formatted(lotteryQualificationStep)));
|
||||
}
|
||||
sql.setParam("activityId", activityId);
|
||||
sql.setParam("step", lotteryQualificationStep);
|
||||
sql.setParam("startDate", startDate);
|
||||
sql.setParam("endDate", endDate);
|
||||
// 保留Sql对象中的绑定参数,云端活动ID作为字符串传入,不能拼入SQL或重新解析toString结果。
|
||||
sql.setCallback(Sqls.callback.maps());
|
||||
dao.execute(sql);
|
||||
userList = sql.getList(NutMap.class);
|
||||
} else if ("everyday".equals(mode)) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
jws.userId AS id,
|
||||
u.username,
|
||||
u.loginname,
|
||||
u.unionid,
|
||||
u.unionname,
|
||||
u.unitid,
|
||||
u.unitname
|
||||
FROM
|
||||
fitness_walk_step jws
|
||||
LEFT JOIN `vw_user` u ON u.id = jws.userId
|
||||
WHERE
|
||||
jws.activityId = @activityId
|
||||
AND jws.step >= @step
|
||||
AND jws.applyDate = @date
|
||||
""");
|
||||
sql.setParam("activityId", activityId);
|
||||
sql.setParam("step", lotteryQualificationStep);
|
||||
sql.setParam("date", DateUtil.today());
|
||||
// 保留Sql对象中的绑定参数,云端活动ID作为字符串传入,不能拼入SQL或重新解析toString结果。
|
||||
sql.setCallback(Sqls.callback.maps());
|
||||
dao.execute(sql);
|
||||
userList = sql.getList(NutMap.class);
|
||||
}
|
||||
|
||||
// 记录达标人员,表明这些人有资格抽奖
|
||||
List<FitnessWalkRaffleUser> raffleUserList = userList.stream().map(v -> {
|
||||
FitnessWalkRaffleUser raffleUser = new FitnessWalkRaffleUser();
|
||||
raffleUser.setId(R.UU32());
|
||||
raffleUser.setActivityId(activityId);
|
||||
raffleUser.setUserId(v.getString("id"));
|
||||
raffleUser.setLoginName(v.getString("loginname"));
|
||||
raffleUser.setUsername(v.getString("username"));
|
||||
if ("custom".equals(mode)) {
|
||||
raffleUser.setStandardsDays(v.getInt("standardsDays"));
|
||||
raffleUser.setAwardSteps(v.getString("total_steps"));
|
||||
}
|
||||
raffleUser.setIsRaffle(false);
|
||||
return raffleUser;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
dao.fastInsert(raffleUserList);
|
||||
|
||||
// 随机抽奖,中奖人数为传递过来的 lotteryUserNum
|
||||
Collections.shuffle(userList);
|
||||
List<NutMap> lotteryUserList = userList.stream().limit(lotteryUserNum).collect(Collectors.toList());
|
||||
|
||||
Date finalStartDate = startDate;
|
||||
Date finalEndDate = endDate;
|
||||
List<FitnessWalkAwardUser> awardLists = lotteryUserList.stream().map(v -> {
|
||||
FitnessWalkAwardUser awardUser = new FitnessWalkAwardUser();
|
||||
awardUser.setActivityId(activityId);
|
||||
awardUser.setUserId(v.getString("id"));
|
||||
awardUser.setUsername(v.getString("username"));
|
||||
awardUser.setLoginName(v.getString("loginname"));
|
||||
awardUser.setAwardName(awardName);
|
||||
awardUser.setUnionId(v.getString("unionid"));
|
||||
awardUser.setUnionName(v.getString("unionname"));
|
||||
awardUser.setUnitId(v.getString("unitid"));
|
||||
awardUser.setUnitName(v.getString("unitname"));
|
||||
awardUser.setApplyDate(new Date());
|
||||
awardUser.setStartDate(finalStartDate);
|
||||
awardUser.setEndDate(finalEndDate);
|
||||
awardUser.setAwardSteps(v.getString("total_steps"));
|
||||
return awardUser;
|
||||
}).collect(Collectors.toList());
|
||||
dao.insert(awardLists);
|
||||
|
||||
// 执行完此定时任务后,禁用此定时任务确保不会下次执行
|
||||
dao.update(Sys_task.class, Chain.make("disabled", 1), Cnd.where("data", "like", "%" + deleteId + "%").and("data", "like", "%" + activityId + "%"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.budwk.app.web.commons.auth.satoken.aop;
|
||||
|
||||
import com.budwk.app.web.commons.base.Globals;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Sa-Token 注解认证白名单工具。
|
||||
* <p>
|
||||
* 用于集中维护不需要登录、权限、角色校验即可访问的 URL,匹配规则与旧 Shiro 体系中的
|
||||
* authIgnoreUrlArr 保持一致:数组中的每一项都按正则表达式与当前请求 URI 完整匹配。
|
||||
*/
|
||||
public class SaTokenAuthIgnoreUtil {
|
||||
|
||||
/**
|
||||
* 不需要认证授权的 URL。
|
||||
* <p>
|
||||
* 参数说明:数组中的字符串为请求 URI 正则表达式,匹配的是去除项目 contextPath 后的路径。
|
||||
* 返回值说明:通过 {@link #isIgnore()} 返回 boolean,true 表示当前请求跳过 Sa-Token 注解校验。
|
||||
*/
|
||||
private static final String[] AUTH_IGNORE_URL_ARR = Lang.array(
|
||||
"/platform/activity/basic/scope/getScopeUser",
|
||||
"/platform/fitnessWalk/stepManage/basic/scope/getScopeUser",
|
||||
"/platform/fitnessWalk/stepManage/getActivityDateStep",
|
||||
"/platform/fitnessWalk/stepManage/winningRecord",
|
||||
"/platform/fitnessWalk/stepManage/updateStepMonth",
|
||||
"/platform/fitnessWalk/stepRanking/getUserStepRanking",
|
||||
"/platform/fitnessWalk/stepManage/getActivityQualifyProgressBar",
|
||||
"/platform/fitnessWalk/stepWining/getUserStepWining",
|
||||
"/platform/fitnessWalk/stepWining/prizeOption",
|
||||
"/platform/fitnessWalk/stepWining/prizeUsers",
|
||||
"/platform/fitnessWalk/punchLottery/judgeWinningToPunch",
|
||||
"/platform/fitnessWalk/punchLottery/getLotteryRecord",
|
||||
"/platform/fitnessWalk/punchLottery/doExchange",
|
||||
"/platform/fitnessWalk/punchLottery/getPunchWining",
|
||||
"/platform/fitnessWalk/punchLottery/doReadLottery",
|
||||
"/platform/fitnessWalk/activityRelation/isCurrentUserRelationActivityQualified");
|
||||
|
||||
/**
|
||||
* 判断当前请求是否命中匿名访问白名单。
|
||||
*
|
||||
* @return boolean true 表示当前 URL 不需要执行登录、权限、角色注解校验;false 表示继续走原有 Sa-Token 校验逻辑
|
||||
*/
|
||||
public static boolean isIgnore() {
|
||||
HttpServletRequest request = Mvcs.getReq();
|
||||
if (request == null) {
|
||||
return false;
|
||||
}
|
||||
String requestURI = Strings.sNull(request.getRequestURI()).replaceFirst("^" + Pattern.quote(Strings.sNull(Globals.AppBase)), "");
|
||||
return Arrays.stream(AUTH_IGNORE_URL_ARR).anyMatch(v -> Pattern.compile(v).matcher(requestURI).matches());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -60,7 +60,7 @@ import java.util.stream.Collectors;
|
||||
* @createTime 2022年01月04日 10:15:00
|
||||
*/
|
||||
@IocBean
|
||||
@At("/platform/activity/basic/scope")
|
||||
@At({"/platform/activity/basic/scope", "/platform/fitnessWalk/stepManage/basic/scope"})
|
||||
@Ok("json:full")
|
||||
@Slf4j
|
||||
public class ActivityBasicScopeController {
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityRelationProjectService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkRelationActivityCompletionService;
|
||||
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.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
/**
|
||||
* 健步走活动关联菜单。
|
||||
* 这里新增“关联项目”管理能力,主活动名称与达标活动数量统一落在 MySQL 中。
|
||||
*/
|
||||
@IocBean
|
||||
@Ok("json")
|
||||
@At("/platform/fitnessWalk/activityRelation")
|
||||
public class FitnessWalkActivityRelationController {
|
||||
|
||||
@Inject
|
||||
private FitnessWalkActivityRelationProjectService relationProjectService;
|
||||
|
||||
@Inject
|
||||
private FitnessWalkRelationActivityCompletionService relationActivityCompletionService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/activity/fitnesswalk/activityRelation.html")
|
||||
@SaCheckPermission("fitnessWalk.activityRelation")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询关联项目分页列表。
|
||||
* @param pageForm 页码、每页条数及查询排序参数
|
||||
* @return Result,data.list为当前页记录,data.totalCount为符合条件的总条数
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("fitnessWalk.activityRelation")
|
||||
public Result pageData(PageForm pageForm) {
|
||||
return Result.success(relationProjectService.pageData(pageForm));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存关联项目。
|
||||
* 支持新增和编辑,保存字段仅包含主活动名称和达标活动数量。
|
||||
* @param relationProject 关联项目配置,包含id、masterActivityName和standardActivityNum
|
||||
* @return Result,code为0表示成功,data为业务结果,msg为操作说明
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("fitnessWalk.activityRelation")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doSave(FitnessWalkActivityRelationProject relationProject) {
|
||||
return relationProjectService.saveRelationProject(relationProject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除关联项目。
|
||||
* @param id 待操作记录的主键ID
|
||||
* @return Result,code为0表示成功,data为业务结果,msg为操作说明
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("fitnessWalk.activityRelation")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doRemove(String id) {
|
||||
return relationProjectService.deleteRelationProject(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看关联项目下已关联的活动。
|
||||
* 活动列表按活动数据中保存的关联ID查询,避免列表展示与实际活动数据不一致。
|
||||
* @param id 待操作记录的主键ID
|
||||
* @return Result,code为0表示成功,data为业务结果,msg为操作说明
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("fitnessWalk.activityRelation")
|
||||
public Result relationActivityList(String id) {
|
||||
return Result.success(relationProjectService.getRelationActivityList(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前登录人是否满足某个关联项目的达标要求。
|
||||
* controller 仅负责接参与返回结果,具体的关联活动和打卡完成判断统一放在独立 service 中处理。
|
||||
* @param relationActivityId 关联项目ID,对应fitness_walk_activity_relation_project主键
|
||||
* @param userId 小程序登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data为业务结果,msg为操作说明
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("fitnessWalk.activityRelation")
|
||||
public Result isCurrentUserRelationActivityQualified(String relationActivityId,String userId) {
|
||||
return Result.success(relationActivityCompletionService.isCurrentUserRelationActivityQualified(relationActivityId,userId));
|
||||
}
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkPunchStatisticsService;
|
||||
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;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 人员打卡情况。
|
||||
* 该页面按人员维度展示所选活动范围内的打卡完成情况,支持主活动聚合查询和分活动单独查询。
|
||||
*/
|
||||
@IocBean
|
||||
@Ok("json")
|
||||
@At("/platform/fitnessWalk/userPunchStatistics")
|
||||
public class FitnessWalkUserPunchStatisticsController {
|
||||
|
||||
@Inject
|
||||
private FitnessWalkPunchStatisticsService fitnessWalkPunchStatisticsService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/activity/fitnesswalk/userPunchStatistics.html")
|
||||
@SaCheckPermission("fitnessWalk.userPunchStatistics")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询活动级联选项。
|
||||
* 这里只返回打卡模式活动,供前端年份切换后联动刷新主活动和分活动下拉。
|
||||
* @param year 四位查询年度
|
||||
* @return Result,code为0表示成功,data为业务结果,msg为操作说明
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("fitnessWalk.userPunchStatistics")
|
||||
public Result getCascadersActivity(@Param("year") int year) {
|
||||
return Result.success(fitnessWalkPunchStatisticsService.userPunchCascaderActivity(year));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询人员打卡情况列表。
|
||||
* 当前接口按人员维度返回点位总数、已打卡点位数以及是否全部完成全部点位。
|
||||
* 当页面只选择主活动且该主活动存在 relationActivityId 时,还支持按达标/未达标筛选人员。
|
||||
* @param mainActivityId 主活动ID或relation:关联项目ID
|
||||
* @param subActivityId 分活动ID,空值表示汇总主活动
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param unionId 分工会ID,空值表示不追加组织筛选
|
||||
* @param searchKeyword 姓名或工号查询内容
|
||||
* @param qualifiedStatus qualified表示已达标,unqualified表示未达标,all表示全部
|
||||
* @param pageNumber 页码,从1开始
|
||||
* @param pageSize 每页记录数
|
||||
* @return Result,data.list为当前页记录,data.totalCount为符合条件的总条数
|
||||
*/
|
||||
@At
|
||||
@Ok("json:full")
|
||||
@SaCheckPermission("fitnessWalk.userPunchStatistics")
|
||||
public Result pageData(String mainActivityId, String subActivityId, String activityId, String unionId, String searchKeyword,
|
||||
String qualifiedStatus, @Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize) {
|
||||
Pagination pagination = fitnessWalkPunchStatisticsService.userPunchPageData(
|
||||
mainActivityId, subActivityId, activityId, unionId, searchKeyword, qualifiedStatus, pageNumber, pageSize
|
||||
);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按主分活动和达标筛选导出全部人员打卡汇总。
|
||||
*
|
||||
* @param mainActivityId 主活动ID或relation:关联项目ID
|
||||
* @param subActivityId 分活动ID,空值表示主活动汇总
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param searchKeyword 姓名、工号或当前查询字段的检索内容
|
||||
* @param qualifiedStatus 达标筛选,qualified、unqualified或all
|
||||
* @param response HTTP响应,用于写入XSSF格式Excel文件流
|
||||
* 无返回对象,通过response写出Excel文件流。
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("fitnessWalk.userPunchStatistics")
|
||||
public void exportExcel(String mainActivityId, String subActivityId, String activityId, String unionId, String searchKeyword, String qualifiedStatus,
|
||||
HttpServletResponse response) {
|
||||
fitnessWalkPunchStatisticsService.exportUserPunchExcel(mainActivityId, subActivityId, activityId, unionId, searchKeyword, qualifiedStatus, response);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.model;
|
||||
|
||||
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.interceptor.annotation.PrevInsert;
|
||||
|
||||
/**
|
||||
* 健步走活动关联项目。
|
||||
* 该表用于保存用户手工创建的“主活动名称 + 达标活动数量”配置,数据统一落在 MySQL 中。
|
||||
*/
|
||||
@Data
|
||||
@Table("fitness_walk_activity_relation_project")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class FitnessWalkActivityRelationProject extends BaseModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Name
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
@Comment("主键ID")
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
@Comment("主活动名称")
|
||||
private String masterActivityName;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.INT)
|
||||
@Comment("达标活动数量")
|
||||
private Integer standardActivityNum;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.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.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import java.util.List;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
/**
|
||||
* 健步走活动关联项目 service。
|
||||
* 负责关联项目在 MySQL 中的分页查询、保存和删除。
|
||||
*/
|
||||
public interface FitnessWalkActivityRelationProjectService extends BaseService<FitnessWalkActivityRelationProject> {
|
||||
|
||||
/**
|
||||
* 查询关联项目列表。
|
||||
*
|
||||
* @param pageForm 分页参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
Pagination pageData(PageForm pageForm);
|
||||
|
||||
/**
|
||||
* 保存关联项目。
|
||||
*
|
||||
* @param relationProject 关联项目
|
||||
* @return 保存结果
|
||||
*/
|
||||
Result saveRelationProject(FitnessWalkActivityRelationProject relationProject);
|
||||
|
||||
/**
|
||||
* 删除关联项目。
|
||||
*
|
||||
* @param id 主键ID
|
||||
* @return 删除结果
|
||||
*/
|
||||
Result deleteRelationProject(String id);
|
||||
|
||||
/**
|
||||
* 查询某个关联项目下的活动列表。
|
||||
* 这里按活动数据中保存的关联ID查询,保证查看结果与活动实际保存的数据一致。
|
||||
*
|
||||
* @param id 关联项目ID
|
||||
* @return 关联活动列表
|
||||
*/
|
||||
List<NutMap> getRelationActivityList(String id);
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service;
|
||||
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityService;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
|
||||
/**
|
||||
* 健步走活动管理 service。
|
||||
* 统一负责活动在 MySQL 中的查询、保存、更新、删除以及云库历史数据同步。
|
||||
*/
|
||||
public interface FitnessWalkActivityService extends BaseService<FitnessWalkActivityRelationProject> {
|
||||
|
||||
/**
|
||||
* 获取当前年份可关联的活动下拉数据。
|
||||
* 下拉内容统一返回活动ID、活动名称和达标活动数量,供创建/编辑页面直接使用。
|
||||
*
|
||||
* @return 关联活动下拉数据
|
||||
*/
|
||||
List<FitnessWalkActivityRelationProject> getCurrentYearRelationActivityOptions();
|
||||
|
||||
/**
|
||||
* 获取福利项目下拉数据。
|
||||
* 创建/编辑活动时统一从 service 层查询福利项目,避免页面直接跨模块拼查询。
|
||||
*
|
||||
* @param year 年份,为空时默认当前年份
|
||||
* @return 福利项目下拉数据,仅返回页面下拉所需字段
|
||||
*/
|
||||
List<NutMap> getWelfareProjectOptions(Integer year);
|
||||
|
||||
/**
|
||||
* 分页检索云端活动并保留当前分工会数据权限。
|
||||
*
|
||||
* @param year 查询年度,四位年份
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param searchName 查询字段,使用页面提供的字段名
|
||||
* @param searchKeyword 姓名、工号或当前查询字段的检索内容
|
||||
* @param pageNumber 页码,从1开始
|
||||
* @param pageSize 每页记录数
|
||||
* @param pageOrderName 列表排序字段
|
||||
* @param pageOrderBy 排序方向,ascending或descending
|
||||
* @return Result,code为0表示成功,data承载分页检索云端活动并保留当前分工会数据权限结果,失败说明见msg
|
||||
*/
|
||||
Result pageData(Integer year, String unionId, String searchName, String searchKeyword, int pageNumber, int pageSize, String pageOrderName, String pageOrderBy);
|
||||
|
||||
/**
|
||||
* 创建云端活动并登记抽奖、连续创建任务和活动缓存。
|
||||
*
|
||||
* @param data 兼容原活动提交参数,活动配置以fitnessWalkActivity为准
|
||||
* @param fitnessWalkActivity 页面提交的活动配置,包含模式、时间、点位和抽奖规则
|
||||
* @param tempFile 活动封面上传文件,不更换时传空
|
||||
* @param stepFiles 计步展示图片文件数组,不更换时传空
|
||||
* @param certificateTempFile 完赛证书背景文件,不更换时传空
|
||||
* @return Result,code为0表示成功,data承载创建云端活动并登记抽奖、连续创建任务和活动缓存结果,失败说明见msg
|
||||
*/
|
||||
Result doAdd(String data, FitnessWalkActivity fitnessWalkActivity,
|
||||
TempFile tempFile,
|
||||
TempFile[] stepFiles,
|
||||
TempFile certificateTempFile);
|
||||
|
||||
/**
|
||||
* 将微信云存储文件ID转换为上传组件回显信息。
|
||||
*
|
||||
* @param pic 微信云存储文件ID
|
||||
* @return Result,code为0表示成功,data承载将微信云存储文件ID转换为上传组件回显信息结果,失败说明见msg
|
||||
*/
|
||||
Result getFile(String pic);
|
||||
|
||||
/**
|
||||
* 更新活动配置并同步关联任务和活动缓存。
|
||||
*
|
||||
* @param fitnessWalkActivity 页面提交的活动配置,包含模式、时间、点位和抽奖规则
|
||||
* @param tempFile 活动封面上传文件,不更换时传空
|
||||
* @param stepFiles 计步展示图片文件数组,不更换时传空
|
||||
* @param certificateTempFile 完赛证书背景文件,不更换时传空
|
||||
* @return Result,code为0表示成功,data承载更新活动配置并同步关联任务和活动缓存结果,失败说明见msg
|
||||
*/
|
||||
Result doEdit(FitnessWalkActivity fitnessWalkActivity,
|
||||
TempFile tempFile,
|
||||
TempFile[] stepFiles,
|
||||
TempFile certificateTempFile);
|
||||
|
||||
/**
|
||||
* 删除云端活动及报名、打卡、礼品券和关联任务。
|
||||
*
|
||||
* @param id 待操作记录的主键ID
|
||||
* @return Result,code为0表示成功,data承载删除云端活动及报名、打卡、礼品券和关联任务结果,失败说明见msg
|
||||
*/
|
||||
Result deleteActivity(String id);
|
||||
|
||||
/**
|
||||
* 检索腾讯地图地址候选项,使用当前项目配置的AppTMapKey与CityName。
|
||||
* @param keyword 地名或地址关键字
|
||||
* @return Result,data为腾讯地图返回的状态及地址候选列表
|
||||
*/
|
||||
Result suggestMapAddress(String keyword);
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service;
|
||||
|
||||
import com.budwk.app.base.result.Result;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 小程序绑定记录公共业务接口。
|
||||
*/
|
||||
public interface FitnessWalkBindingRecordService {
|
||||
|
||||
/**
|
||||
* 按工号、姓名及分工会权限分页检索绑定记录。
|
||||
*
|
||||
* @param keyWord 绑定记录的姓名或工号关键字
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param searchName 查询字段,使用页面提供的字段名
|
||||
* @param searchKeyword 姓名、工号或当前查询字段的检索内容
|
||||
* @param pageNumber 页码,从1开始
|
||||
* @param pageSize 每页记录数
|
||||
* @param pageOrderName 列表排序字段
|
||||
* @param pageOrderBy 排序方向,ascending或descending
|
||||
* @return Result,code为0表示成功,data承载按工号、姓名及分工会权限分页检索绑定记录结果,失败说明见msg
|
||||
*/
|
||||
Result pageData(String keyWord,
|
||||
String unionId,
|
||||
String searchName,
|
||||
String searchKeyword,
|
||||
int pageNumber,int pageSize,
|
||||
String pageOrderName,
|
||||
String pageOrderBy) throws IOException;
|
||||
|
||||
/**
|
||||
* 按记录ID删除选中的云端绑定记录。
|
||||
*
|
||||
* @param ids 待删除记录的主键ID数组
|
||||
* @return Result,code为0表示成功,data承载按记录ID删除选中的云端绑定记录结果,失败说明见msg
|
||||
*/
|
||||
Result delete(String[] ids);
|
||||
|
||||
/**
|
||||
* 清除云端绑定记录,供管理员重新同步人员绑定。
|
||||
*
|
||||
* @return Result,code为0表示成功,data承载清除云端绑定记录,供管理员重新同步人员绑定结果,失败说明见msg
|
||||
*/
|
||||
Result deleteAll();
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service;
|
||||
|
||||
import com.budwk.app.base.result.Result;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 打卡抽奖兑奖公共业务接口。
|
||||
*/
|
||||
public interface FitnessWalkPunchLotteryService {
|
||||
|
||||
/**
|
||||
* 按源版奖池和报名人数分配打卡抽奖结果。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载按源版奖池和报名人数分配打卡抽奖结果结果,失败说明见msg
|
||||
*/
|
||||
Result judgeWinningToPunch(String activityId, String userId);
|
||||
|
||||
/**
|
||||
* 查询用户在指定活动中的打卡抽奖记录。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载查询用户在指定活动中的打卡抽奖记录结果,失败说明见msg
|
||||
*/
|
||||
Result getLotteryRecord(String activityId, String userId);
|
||||
|
||||
/**
|
||||
* 核销奖品并记录兑换时间,重复核销返回状态2。
|
||||
*
|
||||
* @param id 待操作记录的主键ID
|
||||
* @return Result,code为0表示成功,data承载核销奖品并记录兑换时间,重复核销返回状态2结果,失败说明见msg
|
||||
*/
|
||||
Result doExchange(String id);
|
||||
|
||||
/**
|
||||
* 将用户在指定活动中的抽奖记录标记为已读。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载将用户在指定活动中的抽奖记录标记为已读结果,失败说明见msg
|
||||
*/
|
||||
Result doReadLottery(String activityId, String userId);
|
||||
|
||||
/**
|
||||
* 查询指定打卡活动中已中奖的人员。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @return Result,code为0表示成功,data承载查询指定打卡活动中已中奖的人员结果,失败说明见msg
|
||||
*/
|
||||
Result getPunchWining(String activityId);
|
||||
|
||||
/**
|
||||
* 生成不重复的中奖序号,边界规则沿用源版本。
|
||||
*
|
||||
* @param minNum 随机中奖序号下界
|
||||
* @param maxNum 随机中奖序号上界
|
||||
* @param count 需要抽取的中奖序号数量
|
||||
* @return List<Integer>,用于生成不重复的中奖序号,边界规则沿用源版本
|
||||
*/
|
||||
List<Integer> generateRandomNumber(Integer minNum, Integer maxNum, Integer count);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service;
|
||||
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
|
||||
/**
|
||||
* 健步走关联活动完成情况 service。
|
||||
* 该 service 专门处理“按关联项目查询当前登录人的关联活动完成进度”场景,避免把业务判断堆到通用 service 中。
|
||||
*/
|
||||
public interface FitnessWalkRelationActivityCompletionService extends BaseService<FitnessWalkActivityRelationProject> {
|
||||
|
||||
/**
|
||||
* 根据活动关联项目ID,判断当前登录人是否满足关联活动达标要求。
|
||||
* 只有当当前用户完成的达标活动数等于关联配置的 standardActivityNum 时,才返回 true。
|
||||
*
|
||||
* @param relationActivityId 活动关联项目ID
|
||||
* @return true 代表全部合格,false 代表未达标
|
||||
*/
|
||||
boolean isCurrentUserRelationActivityQualified(String relationActivityId,String userId);
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service;
|
||||
|
||||
import com.budwk.app.base.result.Result;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 活动步数与积分公共业务接口。
|
||||
*/
|
||||
public interface FitnessWalkStepManageService {
|
||||
|
||||
/**
|
||||
* 合并微信运动最近30天步数,按每天最大步数保存并计算积分。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @param monthSteps 微信运动步数JSON数组,每项包含step及秒级timestamp
|
||||
* @return Result,code为0表示成功,data承载合并微信运动最近30天步数,按每天最大步数保存并计算积分结果,失败说明见msg
|
||||
*/
|
||||
Result updateStepMonth(String activityId, String userId, String monthSteps);
|
||||
|
||||
/**
|
||||
* 查询活动日期步数,返回step和毫秒级timestamp。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载查询活动日期步数,返回step和毫秒级timestamp结果,失败说明见msg
|
||||
*/
|
||||
Result getActivityDateStep(String activityId, String userId);
|
||||
|
||||
/**
|
||||
* 按每日或自定义规则计算用户达标步数与完成进度。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载按每日或自定义规则计算用户达标步数与完成进度结果,失败说明见msg
|
||||
*/
|
||||
Result getActivityQualifyProgressBar(String activityId, String userId);
|
||||
|
||||
/**
|
||||
* 查询指定用户在活动中的中奖记录。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载查询指定用户在活动中的中奖记录结果,失败说明见msg
|
||||
*/
|
||||
Result winningRecord(String activityId, String userId);
|
||||
|
||||
/**
|
||||
* 标记活动奖项已读,沿用源版活动维度更新范围。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @return Result,code为0表示成功,data承载标记活动奖项已读,沿用源版活动维度更新范围结果,失败说明见msg
|
||||
*/
|
||||
Result hasReadAward(String activityId);
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service;
|
||||
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import java.util.*;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 计步明细统计公共业务接口。
|
||||
*/
|
||||
public interface FitnessWalkStepStatisticsService {
|
||||
|
||||
/**
|
||||
* 按活动、日期、工号及组织条件分页统计用户每日步数。
|
||||
*
|
||||
* @param pageForm 分页、查询字段和排序参数,页码从1开始
|
||||
* @param year 查询年度,四位年份
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param activityDate 起止日期数组,两项均为yyyy-MM-dd
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param unitId 单位ID,空值表示不追加单位筛选
|
||||
* @return Result,code为0表示成功,data承载按活动、日期、工号及组织条件分页统计用户每日步数结果,失败说明见msg
|
||||
*/
|
||||
Result pageData(PageForm pageForm,
|
||||
Integer year,
|
||||
String activityId,
|
||||
String[] activityDate,
|
||||
String unionId,
|
||||
String unitId);
|
||||
|
||||
/**
|
||||
* 按当前活动和日期条件导出计步明细。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param activityDate 起止日期数组,两项均为yyyy-MM-dd
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param unitId 单位ID,空值表示不追加单位筛选
|
||||
* @param response HTTP响应,用于写入XSSF格式Excel文件流
|
||||
* 无返回对象,通过response写出Excel文件流。
|
||||
*/
|
||||
void exportExcel(String activityId,
|
||||
String[] activityDate,
|
||||
String unionId,
|
||||
String unitId, HttpServletResponse response);
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
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.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityRelationProjectService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 健步走活动关联项目 MySQL 实现。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class FitnessWalkActivityRelationProjectServiceImpl extends BaseServiceImpl<FitnessWalkActivityRelationProject>
|
||||
implements FitnessWalkActivityRelationProjectService {
|
||||
|
||||
@Inject
|
||||
private WeAppCloudUtil weAppCloudUtil;
|
||||
|
||||
public FitnessWalkActivityRelationProjectServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination pageData(PageForm pageForm) {
|
||||
PageForm currentPageForm = pageForm == null ? new PageForm() : pageForm;
|
||||
Cnd cnd = buildPageCnd(currentPageForm);
|
||||
Integer pageNumber = currentPageForm.getPageNumber() == null ? 1 : currentPageForm.getPageNumber();
|
||||
Integer pageSize = currentPageForm.getPageSize() == null ? 10 : currentPageForm.getPageSize();
|
||||
return listPageMap(pageNumber, pageSize, cnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result saveRelationProject(FitnessWalkActivityRelationProject relationProject) {
|
||||
try {
|
||||
validateAndNormalize(relationProject);
|
||||
if (StrUtil.isBlank(relationProject.getId())) {
|
||||
insert(relationProject);
|
||||
} else {
|
||||
updateIgnoreNull(relationProject);
|
||||
}
|
||||
return Result.success("保存成功");
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result deleteRelationProject(String id) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
return Result.error("未获取到需要删除的关联项目");
|
||||
}
|
||||
delete(id);
|
||||
return Result.success("删除成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> getRelationActivityList(String id) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
StringBuilder sql = new StringBuilder("db.collection('activity').where({relationActivityId:'")
|
||||
.append(id)
|
||||
.append("'");
|
||||
|
||||
sql.append("}).field({")
|
||||
.append("masterActivityName:true,")
|
||||
.append("branchActivityName:true,")
|
||||
.append("activityModel:true,")
|
||||
.append("createdAt:true,")
|
||||
.append("relationActivityId:true")
|
||||
.append("}).orderBy('createdAt','desc').limit(1000).get()");
|
||||
|
||||
try {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString());
|
||||
return jsonObject.getJSONArray("data").stream()
|
||||
.map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class))
|
||||
.map(this::buildRelationActivityItem)
|
||||
.collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("查询关联活动失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装分页查询条件。
|
||||
* 当前列表仅支持按主活动名称模糊查询,并默认按创建时间倒序展示。
|
||||
*/
|
||||
private Cnd buildPageCnd(PageForm pageForm) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (pageForm != null && StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
|
||||
cnd.and("masterActivityName", "like", "%" + pageForm.getSearchKeyword().trim() + "%");
|
||||
}
|
||||
cnd.desc("createdAt");
|
||||
return cnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前统一校验和标准化字段。
|
||||
* 这里同时限制主活动名称唯一,避免重复创建同名关联项目影响后续配置。
|
||||
*/
|
||||
private void validateAndNormalize(FitnessWalkActivityRelationProject relationProject) {
|
||||
if (relationProject == null) {
|
||||
throw new RuntimeException("关联项目信息不能为空");
|
||||
}
|
||||
|
||||
String masterActivityName = Strings.sNull(relationProject.getMasterActivityName()).trim();
|
||||
if (Strings.isBlank(masterActivityName)) {
|
||||
throw new RuntimeException("请填写主活动名称");
|
||||
}
|
||||
relationProject.setMasterActivityName(masterActivityName);
|
||||
|
||||
if (relationProject.getStandardActivityNum() == null || relationProject.getStandardActivityNum() <= 0) {
|
||||
throw new RuntimeException("达标活动数量必须大于0");
|
||||
}
|
||||
|
||||
Cnd cnd = Cnd.where("masterActivityName", "=", masterActivityName);
|
||||
if (Strings.isNotBlank(relationProject.getId())) {
|
||||
cnd.and("id", "<>", relationProject.getId());
|
||||
}
|
||||
if (count(cnd) > 0) {
|
||||
throw new RuntimeException("主活动名称已存在,请勿重复创建");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将活动对象转换为查看页需要的展示数据。
|
||||
* 这里统一补充活动名称和活动模式文本,前端只负责展示,不再重复拼接字段。
|
||||
*/
|
||||
private NutMap buildRelationActivityItem(FitnessWalkActivity activity) {
|
||||
NutMap item = NutMap.NEW();
|
||||
item.put("activityId", activity.get_id());
|
||||
item.put("activityName", buildRelationActivityName(activity));
|
||||
item.put("activityModel", activity.getActivityModel());
|
||||
item.put("activityModelName", getActivityModelName(activity.getActivityModel()));
|
||||
item.put("createdAt", activity.getCreatedAt());
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一生成活动展示名称。
|
||||
* 没有分活动名称时仅显示主活动名称;有分活动名称时按“主活动(分活动)”展示。
|
||||
*/
|
||||
private String buildRelationActivityName(FitnessWalkActivity activity) {
|
||||
String masterActivityName = Strings.sNull(activity.getMasterActivityName()).trim();
|
||||
String branchActivityName = Strings.sNull(activity.getBranchActivityName()).trim();
|
||||
if (Strings.isBlank(branchActivityName)) {
|
||||
return masterActivityName;
|
||||
}
|
||||
return masterActivityName + "(" + branchActivityName + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回活动模式中文名称,便于查看页面直接展示。
|
||||
*/
|
||||
private String getActivityModelName(String activityModel) {
|
||||
if ("punch".equals(activityModel)) {
|
||||
return "打卡模式";
|
||||
}
|
||||
if ("stepCount".equals(activityModel)) {
|
||||
return "计步模式";
|
||||
}
|
||||
if ("GPS".equals(activityModel)) {
|
||||
return "GPS模式";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.dao.CndPlus;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.sys.models.Sys_config;
|
||||
import com.budwk.app.sys.models.Sys_task;
|
||||
import com.budwk.app.sys.services.SysConfigService;
|
||||
import com.budwk.app.sys.services.SysTaskService;
|
||||
import com.budwk.app.task.services.TaskPlatformService;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil;
|
||||
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.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 健步走活动管理实现。
|
||||
* 负责活动读写、关联项目及福利下拉、腾讯地图地址检索。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class FitnessWalkActivityServiceImpl extends BaseServiceImpl<FitnessWalkActivityRelationProject> implements FitnessWalkActivityService {
|
||||
|
||||
@Inject
|
||||
private FitnessWalkCommonService fitnessWalkCommonService;
|
||||
|
||||
@Inject
|
||||
private SysTaskService sysTaskService;
|
||||
|
||||
@Inject
|
||||
private TaskPlatformService taskPlatformService;
|
||||
|
||||
@Inject
|
||||
private WeAppCloudUtil weAppCloudUtil;
|
||||
|
||||
public FitnessWalkActivityServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FitnessWalkActivityRelationProject> getCurrentYearRelationActivityOptions() {
|
||||
List<FitnessWalkActivityRelationProject> projectList = dao().query(FitnessWalkActivityRelationProject.class, CndPlus.create().andEX("YEAR(FROM_UNIXTIME(createdAt/ 1000))", "=", DateUtil.thisYear()));
|
||||
return projectList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> getWelfareProjectOptions(Integer year) {
|
||||
int targetYear = year == null ? DateUtil.thisYear() : year;
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id,
|
||||
`name`
|
||||
FROM
|
||||
welfare_project
|
||||
$condition
|
||||
""");
|
||||
CndPlus cnd = CndPlus.create();
|
||||
cnd.and("isDisabled", "=", false);
|
||||
cnd.andEX("year", "=", targetYear);
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页检索云端活动并保留当前分工会数据权限。
|
||||
*
|
||||
* @param year 查询年度,四位年份
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param searchName 查询字段,使用页面提供的字段名
|
||||
* @param searchKeyword 姓名、工号或当前查询字段的检索内容
|
||||
* @param pageNumber 页码,从1开始
|
||||
* @param pageSize 每页记录数
|
||||
* @param pageOrderName 列表排序字段
|
||||
* @param pageOrderBy 排序方向,ascending或descending
|
||||
* @return Result,code为0表示成功,data承载分页检索云端活动并保留当前分工会数据权限结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result pageData(Integer year, String unionId, String searchName, String searchKeyword, int pageNumber, int pageSize, String pageOrderName, String pageOrderBy) {
|
||||
|
||||
int skip = (pageNumber == 1 ? 0 : (pageNumber - 1)) * pageSize;
|
||||
|
||||
StringBuilder sql = new StringBuilder("db.collection('activity')");
|
||||
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
|
||||
sql.append("where(");
|
||||
sql.append("_.or([");
|
||||
sql.append("{name:{$regex:'").append(searchKeyword).append("',$options:'i'}}");
|
||||
sql.append("{tag:{$regex:'").append(searchKeyword).append("',$options:'i'}}");
|
||||
sql.append("])");
|
||||
sql.append(")");
|
||||
}
|
||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
sql.append(".where({unionId:'").append(SecurityUtil.getUnionId()).append("'})");
|
||||
} else if (StrUtil.isNotBlank(unionId)) {
|
||||
sql.append(".where({unionId:'").append(unionId).append("'})");
|
||||
}
|
||||
sql.append(".skip(").append(skip).append(")");
|
||||
sql.append(".limit(").append(pageSize).append(")");
|
||||
sql.append(".get()");
|
||||
try {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString());
|
||||
List<Object> data = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v, FitnessWalkActivity.class)).collect(Collectors.toList());
|
||||
JSONObject pager = jsonObject.getJSONObject("pager");
|
||||
return Result.success(new Pagination(pageNumber, pageSize, pager.getIntValue("Total"), data));
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建云端活动并登记抽奖、连续创建任务和活动缓存。
|
||||
*
|
||||
* @param data 兼容原活动提交参数,活动配置以fitnessWalkActivity为准
|
||||
* @param fitnessWalkActivity 页面提交的活动配置,包含模式、时间、点位和抽奖规则
|
||||
* @param tempFile 活动封面上传文件,不更换时传空
|
||||
* @param stepFiles 计步展示图片文件数组,不更换时传空
|
||||
* @param certificateTempFile 完赛证书背景文件,不更换时传空
|
||||
* @return Result,code为0表示成功,data承载创建云端活动并登记抽奖、连续创建任务和活动缓存结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doAdd(String data, FitnessWalkActivity fitnessWalkActivity,
|
||||
TempFile tempFile,
|
||||
TempFile[] stepFiles,
|
||||
TempFile certificateTempFile) {
|
||||
try {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
fitnessWalkActivity.setCreatedAt(currentTime);
|
||||
fitnessWalkActivity.setUpdatedAt(currentTime);
|
||||
|
||||
//设置时间
|
||||
if ("punch".equals(fitnessWalkActivity.getActivityModel())) {
|
||||
fitnessWalkActivity.setApplyStartTime(fitnessWalkActivity.getApplyTime()[0]);
|
||||
fitnessWalkActivity.setApplyEndTime(fitnessWalkActivity.getApplyTime()[1]);
|
||||
}
|
||||
fitnessWalkActivity.setStartTime(fitnessWalkActivity.getTime()[0]);
|
||||
fitnessWalkActivity.setEndTime(fitnessWalkActivity.getTime()[1]);
|
||||
|
||||
if (tempFile != null) {
|
||||
String fileId = weAppCloudUtil.uploadFile("activity/", tempFile.getFile());
|
||||
fitnessWalkActivity.setCover(fileId);
|
||||
}
|
||||
|
||||
if (Lang.isNotEmpty(stepFiles)) {
|
||||
List<String> stepFileIdList = new ArrayList<>();
|
||||
for (TempFile file : stepFiles) {
|
||||
String fileId = weAppCloudUtil.uploadFile("activity/", file.getFile());
|
||||
stepFileIdList.add(fileId);
|
||||
}
|
||||
fitnessWalkActivity.setRandomPics(stepFileIdList);
|
||||
}
|
||||
|
||||
if (certificateTempFile != null) {
|
||||
String fileId = weAppCloudUtil.uploadFile("activity/", certificateTempFile.getFile());
|
||||
fitnessWalkActivity.setCompletionCertificateCover(fileId);
|
||||
}
|
||||
|
||||
if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) {
|
||||
fitnessWalkActivity.setUnionId(SecurityUtil.getUnionId());
|
||||
} else {
|
||||
fitnessWalkActivity.setUnionId("");
|
||||
}
|
||||
|
||||
fitnessWalkActivity.setNote(fitnessWalkActivity.getNote().replace("\"", "'"));
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("db.collection('activity').add({data:").append(Json.toJson(fitnessWalkActivity)).append("})");
|
||||
|
||||
try {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.INSERT, sql.toString());
|
||||
// 新增成功后回填云库生成的活动ID,后续创建定时任务和刷新缓存都依赖这个ID。
|
||||
if (jsonObject.containsKey("id_list") && jsonObject.getJSONArray("id_list") != null && !jsonObject.getJSONArray("id_list").isEmpty()) {
|
||||
fitnessWalkActivity.set_id(jsonObject.getJSONArray("id_list").getString(0));
|
||||
}
|
||||
fitnessWalkCommonService.addLotteryTask(fitnessWalkActivity);
|
||||
fitnessWalkCommonService.addCreateActivityTask(fitnessWalkActivity);
|
||||
fitnessWalkCommonService.addOrEditDoSaveRedis(fitnessWalkActivity);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将微信云存储文件ID转换为上传组件回显信息。
|
||||
*
|
||||
* @param pic 微信云存储文件ID
|
||||
* @return Result,code为0表示成功,data承载将微信云存储文件ID转换为上传组件回显信息结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result getFile(String pic) {
|
||||
try {
|
||||
NutMap res = NutMap.NEW();
|
||||
String imgurl = weAppCloudUtil.httpFile(pic);
|
||||
HashMap<String, Object> imgmap = new HashMap<>();
|
||||
imgmap.put("status", "success");
|
||||
imgmap.put("name", imgurl.substring(imgurl.lastIndexOf("/") + 1));
|
||||
imgmap.put("id", imgurl);
|
||||
imgmap.put("url", imgurl);
|
||||
res.setv("img", imgmap);
|
||||
return Result.success(res);
|
||||
} catch (Exception e) {
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新活动配置并同步关联任务和活动缓存。
|
||||
*
|
||||
* @param fitnessWalkActivity 页面提交的活动配置,包含模式、时间、点位和抽奖规则
|
||||
* @param tempFile 活动封面上传文件,不更换时传空
|
||||
* @param stepFiles 计步展示图片文件数组,不更换时传空
|
||||
* @param certificateTempFile 完赛证书背景文件,不更换时传空
|
||||
* @return Result,code为0表示成功,data承载更新活动配置并同步关联任务和活动缓存结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doEdit(FitnessWalkActivity fitnessWalkActivity,
|
||||
TempFile tempFile,
|
||||
TempFile[] stepFiles,
|
||||
TempFile certificateTempFile) {
|
||||
fitnessWalkActivity.setUpdatedAt(System.currentTimeMillis());
|
||||
|
||||
//设置时间
|
||||
if ("punch".equals(fitnessWalkActivity.getActivityModel())) {
|
||||
fitnessWalkActivity.setApplyStartTime(fitnessWalkActivity.getApplyTime()[0]);
|
||||
fitnessWalkActivity.setApplyEndTime(fitnessWalkActivity.getApplyTime()[1]);
|
||||
}
|
||||
fitnessWalkActivity.setStartTime(fitnessWalkActivity.getTime()[0]);
|
||||
fitnessWalkActivity.setEndTime(fitnessWalkActivity.getTime()[1]);
|
||||
|
||||
if (tempFile != null) {
|
||||
String fileId = weAppCloudUtil.uploadFile("activity/", tempFile.getFile());
|
||||
fitnessWalkActivity.setCover(fileId);
|
||||
}
|
||||
|
||||
if (Lang.isNotEmpty(stepFiles)) {
|
||||
List<String> stepFileIdList = new ArrayList<>();
|
||||
for (TempFile file : stepFiles) {
|
||||
String fileId = weAppCloudUtil.uploadFile("activity/", file.getFile());
|
||||
stepFileIdList.add(fileId);
|
||||
}
|
||||
fitnessWalkActivity.setRandomPics(stepFileIdList);
|
||||
}
|
||||
|
||||
if (certificateTempFile != null) {
|
||||
String fileId = weAppCloudUtil.uploadFile("activity/", certificateTempFile.getFile());
|
||||
fitnessWalkActivity.setCompletionCertificateCover(fileId);
|
||||
}
|
||||
|
||||
if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) {
|
||||
fitnessWalkActivity.setUnionId(SecurityUtil.getUnionId());
|
||||
} else {
|
||||
fitnessWalkActivity.setUnionId("");
|
||||
}
|
||||
|
||||
fitnessWalkActivity.setNote(fitnessWalkActivity.getNote().replace("\"", "'"));
|
||||
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("db.collection('activity').doc('")
|
||||
.append(fitnessWalkActivity.get_id())
|
||||
.append("').update({data:").append(Json.toJson(fitnessWalkActivity)).append("})");
|
||||
|
||||
try {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.UPDATE, sql.toString());
|
||||
fitnessWalkCommonService.addLotteryTask(fitnessWalkActivity);
|
||||
fitnessWalkCommonService.addCreateActivityTask(fitnessWalkActivity);
|
||||
fitnessWalkCommonService.addOrEditDoSaveRedis(fitnessWalkActivity);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除云端活动及报名、打卡、礼品券和关联任务。
|
||||
*
|
||||
* @param id 待操作记录的主键ID
|
||||
* @return Result,code为0表示成功,data承载删除云端活动及报名、打卡、礼品券和关联任务结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result deleteActivity(String id) {
|
||||
try {
|
||||
weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, "db.collection('activity').doc('" + id + "').remove()");
|
||||
weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, "db.collection('register').where({activity_id:'" + id + "'}).remove()");
|
||||
weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, "db.collection('sign').where({activity_id:'" + id + "'}).remove()");
|
||||
weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, "db.collection('gift_voucher').where({activity_id:'" + id + "'}).remove()");
|
||||
|
||||
List<Sys_task> sys_tasks = sysTaskService.query(Cnd.where("note", "=", id));
|
||||
if (sys_tasks != null) {
|
||||
sys_tasks.forEach(v -> {
|
||||
taskPlatformService.delete(v.getId(), v.getId());
|
||||
sysTaskService.delete(v.getId());
|
||||
});
|
||||
|
||||
}
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
private SysConfigService sysConfigService;
|
||||
|
||||
/**
|
||||
* 从腾讯地图检索候选地址,Key读取当前项目配置,城市用于限定搜索区域。
|
||||
* @param keyword 地名或地址关键字
|
||||
* @return Result,data.status为地图状态,data.data为地址候选数组
|
||||
*/
|
||||
@Override
|
||||
public Result suggestMapAddress(String keyword) {
|
||||
Sys_config keyConfig = sysConfigService.getValueByKey("AppTMapKey");
|
||||
String mapKey = keyConfig == null ? "" : keyConfig.getConfigValue();
|
||||
if (Strings.isBlank(mapKey)) {
|
||||
return Result.error("请先配置腾讯地图系统参数AppTMapKey");
|
||||
}
|
||||
Sys_config cityConfig = sysConfigService.getValueByKey("CityName");
|
||||
String cityName = cityConfig == null ? "" : Strings.sNull(cityConfig.getConfigValue());
|
||||
// 参数编码由HttpUtil完成,地址中的空格和&不得改变请求参数含义。
|
||||
String response = HttpUtil.get("https://apis.map.qq.com/ws/place/v1/suggestion",
|
||||
Map.of("key", mapKey, "keyword", keyword, "region", cityName));
|
||||
return Result.success(JSON.parseObject(response));
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkBindingRecordService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil;
|
||||
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.json.Json;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 小程序绑定记录业务服务,集中处理查询、数据写入和导出。
|
||||
*/
|
||||
@IocBean
|
||||
public class FitnessWalkBindingRecordServiceImpl implements FitnessWalkBindingRecordService {
|
||||
|
||||
@Inject
|
||||
private WeAppCloudUtil weAppCloudUtil;
|
||||
|
||||
/**
|
||||
* 按工号、姓名及分工会权限分页检索绑定记录。
|
||||
*
|
||||
* @param keyWord 绑定记录的姓名或工号关键字
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param searchName 查询字段,使用页面提供的字段名
|
||||
* @param searchKeyword 姓名、工号或当前查询字段的检索内容
|
||||
* @param pageNumber 页码,从1开始
|
||||
* @param pageSize 每页记录数
|
||||
* @param pageOrderName 列表排序字段
|
||||
* @param pageOrderBy 排序方向,ascending或descending
|
||||
* @return Result,code为0表示成功,data承载按工号、姓名及分工会权限分页检索绑定记录结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result pageData(String keyWord,
|
||||
String unionId,
|
||||
String searchName,
|
||||
String searchKeyword,
|
||||
int pageNumber,int pageSize,
|
||||
String pageOrderName,
|
||||
String pageOrderBy) throws IOException {
|
||||
int skip = (pageNumber == 1 ? 0 : (pageNumber - 1)) * pageSize;
|
||||
|
||||
StringBuilder sql = new StringBuilder("db.collection('login_record')");
|
||||
if (StrUtil.isNotBlank(keyWord)) {
|
||||
sql.append(".where(");
|
||||
sql.append("_.or([");
|
||||
sql.append("{loginname:{$regex:'").append(keyWord).append("',$options:'i'}},");
|
||||
sql.append("{username:{$regex:'").append(keyWord).append("',$options:'i'}}");
|
||||
sql.append("])");
|
||||
sql.append(")");
|
||||
}
|
||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
sql.append(".where({'data.unionid':'").append(SecurityUtil.getUnionId()).append("'})");
|
||||
} else {
|
||||
if (Strings.isNotBlank(unionId)) {
|
||||
sql.append(".where({'data.unionid':'").append(unionId).append("'})");
|
||||
}
|
||||
}
|
||||
sql.append(".field({ data: false })");
|
||||
sql.append(".skip(").append(skip).append(")");
|
||||
sql.append(".limit(").append(pageSize).append(")");
|
||||
sql.append(".get()");
|
||||
|
||||
try {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString());
|
||||
List<NutMap> data = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v, NutMap.class)).collect(Collectors.toList());
|
||||
JSONObject pager = jsonObject.getJSONObject("pager");
|
||||
return Result.success(new Pagination(pageNumber, pageSize, pager.getIntValue("Total"), data));
|
||||
} catch (Exception e){
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按记录ID删除选中的云端绑定记录。
|
||||
*
|
||||
* @param ids 待删除记录的主键ID数组
|
||||
* @return Result,code为0表示成功,data承载按记录ID删除选中的云端绑定记录结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result delete(String[] ids) {
|
||||
try {
|
||||
String sql = "db.collection('login_record').where({_id:_.in(" + Json.toJson(ids) + ")}).remove()";
|
||||
weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE,sql);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除云端绑定记录,供管理员重新同步人员绑定。
|
||||
*
|
||||
* @return Result,code为0表示成功,data承载清除云端绑定记录,供管理员重新同步人员绑定结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result deleteAll() {
|
||||
try {
|
||||
String sql = "db.collection('login_record').where({_id: _.neq('0')}).remove()";
|
||||
weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE,sql);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkPunchLottery;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkPunchLotteryService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.random.R;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 打卡抽奖兑奖业务服务,集中处理查询、数据写入和导出。
|
||||
*/
|
||||
@IocBean
|
||||
public class FitnessWalkPunchLotteryServiceImpl implements FitnessWalkPunchLotteryService {
|
||||
private final ReentrantLock lock = new ReentrantLock(true);
|
||||
|
||||
@Inject
|
||||
private RedisService redisService;
|
||||
|
||||
@Inject
|
||||
private WeAppCloudUtil weAppCloudUtil;
|
||||
|
||||
@Inject
|
||||
private FitnessWalkCommonService fitnessWalkCommonService;
|
||||
|
||||
/**
|
||||
* 按源版奖池和报名人数分配打卡抽奖结果。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载按源版奖池和报名人数分配打卡抽奖结果结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result judgeWinningToPunch(String activityId, String userId) {
|
||||
try {
|
||||
if (StrUtil.isBlank(activityId) && StrUtil.isBlank(userId)) {
|
||||
return Result.error("参数错误");
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
int winingCount = fitnessWalkCommonService.dao().count(FitnessWalkPunchLottery.class, Cnd.where("activityId", "=", activityId).and("userId", "=", userId));
|
||||
if (winingCount > 0) {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
String redisKey = this.getClass().getName() + "#judgeWinningToPunch#activityid:" + activityId + "#PRIZELIST:";
|
||||
String getPrizeIndexesKey = this.getClass().getName() + "#judgeWinningToPunch#activityid:" + activityId + "#GETPRIZEINDEXES:";
|
||||
|
||||
// redisService.del(getPrizeIndexesKey);
|
||||
List<FitnessWalkActivity.PunchLotteryPrize> punchLotteryPrizes = new ArrayList<>();
|
||||
String punchLotteryPrizesJson = redisService.get(redisKey);
|
||||
if (StrUtil.isNotBlank(punchLotteryPrizesJson)) {
|
||||
punchLotteryPrizes = JSON.parseArray(punchLotteryPrizesJson, FitnessWalkActivity.PunchLotteryPrize.class);
|
||||
} else {
|
||||
JSONObject prizeJsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, "db.collection('activity').doc('%s').field({punchLotteryPrizes: true}).get()".formatted(activityId));
|
||||
List<FitnessWalkActivity.PunchLotteryPrize> prizeList = JSON.parseObject((String) prizeJsonObject.getJSONArray("data").get(0)).getJSONArray("punchLotteryPrizes").toJavaList(FitnessWalkActivity.PunchLotteryPrize.class);
|
||||
redisService.set(redisKey, JSON.toJSONString(prizeList));
|
||||
punchLotteryPrizes = prizeList;
|
||||
}
|
||||
|
||||
List<Integer> winningUserIndexList = null;
|
||||
//奖项,抽取多少个
|
||||
int sum = punchLotteryPrizes.stream().mapToInt(v -> Integer.parseInt(v.getNum())).sum();
|
||||
//判断是否有key,可以获得奖项的下标数组
|
||||
if (!redisService.exists(getPrizeIndexesKey)) {
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, "db.collection('register').where({activity_id:'%s'}).count()".formatted(activityId));
|
||||
JSONObject pager = jsonObject.getJSONObject("pager");
|
||||
String total = pager.getString("Total");
|
||||
//报名人数的一半,从这里的人中取数(最多抽取人数)
|
||||
Integer lotteryNum = (int) Math.ceil(Double.parseDouble(total) / 2);
|
||||
winningUserIndexList = generateRandomNumber(1, lotteryNum, sum);
|
||||
redisService.set(getPrizeIndexesKey, JSON.toJSONString(winningUserIndexList));
|
||||
} else {
|
||||
String data = redisService.get(getPrizeIndexesKey);
|
||||
winningUserIndexList = JSONObject.parseArray(data, Integer.class);
|
||||
}
|
||||
|
||||
List<FitnessWalkPunchLottery> punchLotteryList = fitnessWalkCommonService.dao().query(FitnessWalkPunchLottery.class, Cnd.where("activityId", "=", activityId));
|
||||
|
||||
Sys_user user = fitnessWalkCommonService.dao().fetch(Sys_user.class, Cnd.where("id", "=", userId));
|
||||
|
||||
FitnessWalkPunchLottery punchLottery = new FitnessWalkPunchLottery();
|
||||
punchLottery.setId(R.UU32());
|
||||
punchLottery.setActivityId(activityId);
|
||||
punchLottery.setUserId(userId);
|
||||
punchLottery.setLoginName(user.getLoginname());
|
||||
punchLottery.setUserName(user.getUsername());
|
||||
punchLottery.setWinDate(new Date());
|
||||
punchLottery.setIsRead(false);
|
||||
punchLottery.setIsWin(false);
|
||||
punchLottery.setIsExchange(false);
|
||||
|
||||
if (winningUserIndexList.contains((punchLotteryList.size()))) {
|
||||
|
||||
//奖项map
|
||||
Map<String, String> prizeMap = punchLotteryPrizes.stream().collect(Collectors.toMap(v -> v.getValue(), v -> v.getName()));
|
||||
|
||||
//奖项池
|
||||
List<String> prizeIdList = new ArrayList<>();
|
||||
|
||||
punchLotteryPrizes.forEach(v -> {
|
||||
for (int i = 0; i < Integer.parseInt(v.getNum()); i++) {
|
||||
prizeIdList.add(v.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
//已经中奖的奖项ID集合
|
||||
List<String> winPrizeIdList = punchLotteryList.stream().filter(v -> v.getIsWin()).map(v -> v.getPrizeId()).collect(Collectors.toList());
|
||||
winPrizeIdList.forEach(prizeId -> {
|
||||
int i = prizeIdList.indexOf(prizeId);
|
||||
prizeIdList.remove(i);
|
||||
});
|
||||
|
||||
Random random = new Random();
|
||||
if (prizeIdList.size() != 0) {
|
||||
int index = random.nextInt(prizeIdList.size());
|
||||
|
||||
// 获取随机元素
|
||||
String winingPrizeId = prizeIdList.get(index);
|
||||
String winingPrizeName = prizeMap.get(winingPrizeId);
|
||||
punchLottery.setPrizeName(winingPrizeName);
|
||||
punchLottery.setPrizeId(winingPrizeId);
|
||||
punchLottery.setIsWin(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fitnessWalkCommonService.dao().insert(punchLottery);
|
||||
if (punchLottery.getIsWin()) {
|
||||
return Result.success().addData(punchLottery.getPrizeId());
|
||||
} else {
|
||||
return Result.success().addData(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error(e.getMessage());
|
||||
} finally {
|
||||
if (lock.isLocked()) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户在指定活动中的打卡抽奖记录。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载查询用户在指定活动中的打卡抽奖记录结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result getLotteryRecord(String activityId, String userId) {
|
||||
List<FitnessWalkPunchLottery> list = fitnessWalkCommonService.dao().query(FitnessWalkPunchLottery.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("userId", "=", userId));
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 核销奖品并记录兑换时间,重复核销返回状态2。
|
||||
*
|
||||
* @param id 待操作记录的主键ID
|
||||
* @return Result,code为0表示成功,data承载核销奖品并记录兑换时间,重复核销返回状态2结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doExchange(String id) {
|
||||
|
||||
int count = fitnessWalkCommonService.dao().count(FitnessWalkPunchLottery.class, Cnd.where("id", "=", id).and("isExchange", "=", 1));
|
||||
//返回2 "该二维码已兑奖!"
|
||||
if (count > 0) {
|
||||
return Result.success(2);
|
||||
}
|
||||
|
||||
int update = fitnessWalkCommonService.dao().update(FitnessWalkPunchLottery.class,
|
||||
Chain.make("isExchange", 1).add("exchangeDate", new Date()),
|
||||
Cnd.where("id", "=", id));
|
||||
return update > 0 ? Result.success() : Result.error();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户在指定活动中的抽奖记录标记为已读。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载将用户在指定活动中的抽奖记录标记为已读结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doReadLottery(String activityId, String userId) {
|
||||
fitnessWalkCommonService.dao().update(FitnessWalkPunchLottery.class, Chain.make("isRead", 1),
|
||||
Cnd.where("userId", "=", userId).and("activityId", "=", activityId));
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定打卡活动中已中奖的人员。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @return Result,code为0表示成功,data承载查询指定打卡活动中已中奖的人员结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result getPunchWining(String activityId) {
|
||||
List<FitnessWalkPunchLottery> punchLotteryList = fitnessWalkCommonService.dao().query(FitnessWalkPunchLottery.class,
|
||||
Cnd.where("isWin", "=", 1).and("activityId", "=", activityId));
|
||||
return Result.success(punchLotteryList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成不重复的中奖序号,边界规则沿用源版本。
|
||||
*
|
||||
* @param minNum 随机中奖序号下界
|
||||
* @param maxNum 随机中奖序号上界
|
||||
* @param count 需要抽取的中奖序号数量
|
||||
* @return List<Integer>,用于生成不重复的中奖序号,边界规则沿用源版本
|
||||
*/
|
||||
@Override
|
||||
public List<Integer> generateRandomNumber(Integer minNum, Integer maxNum, Integer count) {
|
||||
Set<Integer> randomNumbers = new HashSet<>();
|
||||
Random rand = new Random();
|
||||
//如果是 抽奖人数小于了 奖品数量,会陷入死循环
|
||||
if (maxNum <= count) {
|
||||
for (int i = 0; i <= maxNum; i++) {
|
||||
randomNumbers.add(i + 1);
|
||||
}
|
||||
} else {
|
||||
while (randomNumbers.size() < count) {
|
||||
int randomNum = rand.nextInt(maxNum - minNum + 1) + minNum;
|
||||
randomNumbers.add(randomNum);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(randomNumbers);
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkRelationActivityCompletionService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil;
|
||||
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 org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 健步走关联活动完成情况实现。
|
||||
* 这里统一处理“查关联活动配置 + 查当前登录人的打卡情况 + 计算是否满足达标数量”的完整流程。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class FitnessWalkRelationActivityCompletionServiceImpl extends BaseServiceImpl<FitnessWalkActivityRelationProject>
|
||||
implements FitnessWalkRelationActivityCompletionService {
|
||||
|
||||
@Inject
|
||||
private WeAppCloudUtil weAppCloudUtil;
|
||||
|
||||
public FitnessWalkRelationActivityCompletionServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联项目ID查询当前登录人的关联活动完成情况。
|
||||
* 只有当前用户在某个活动中完成了全部点位打卡,该活动才计入“已达标活动数”。
|
||||
* 最终必须达到关联项目配置的 standardActivityNum,才算整体合格。
|
||||
* @param relationActivityId 关联项目ID,对应fitness_walk_activity_relation_project主键
|
||||
* @param userId 小程序登录接口返回的用户ID
|
||||
*/
|
||||
@Override
|
||||
public boolean isCurrentUserRelationActivityQualified(String relationActivityId,String userId) {
|
||||
if (Strings.isBlank(relationActivityId)) {
|
||||
throw new RuntimeException("活动关联ID不能为空");
|
||||
}
|
||||
|
||||
if (Strings.isBlank(userId)) {
|
||||
throw new RuntimeException("未获取到当前登录人");
|
||||
}
|
||||
|
||||
FitnessWalkActivityRelationProject relationProject = dao().fetch(FitnessWalkActivityRelationProject.class,
|
||||
Cnd.where("id", "=", relationActivityId));
|
||||
if (relationProject == null) {
|
||||
throw new RuntimeException("未找到对应的活动关联配置");
|
||||
}
|
||||
|
||||
List<FitnessWalkActivity> relationActivityList = queryRelationActivityList(relationActivityId);
|
||||
Map<String, Integer> signedPtsNumMap = queryUserSignedPtsNumMap(userId, relationActivityList);
|
||||
|
||||
int qualifiedActivityNum = (int) relationActivityList.stream()
|
||||
.filter(activity -> isActivityCompleted(activity, signedPtsNumMap))
|
||||
.count();
|
||||
int standardActivityNum = relationProject.getStandardActivityNum() == null ? 0 : relationProject.getStandardActivityNum();
|
||||
return qualifiedActivityNum == standardActivityNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个关联项目下的所有活动。
|
||||
* 这里只取完成情况计算需要的字段,避免无关字段增加云函数返回体积。
|
||||
*/
|
||||
private List<FitnessWalkActivity> queryRelationActivityList(String relationActivityId) {
|
||||
StringBuilder sql = new StringBuilder("db.collection('activity').where({relationActivityId:'")
|
||||
.append(relationActivityId)
|
||||
.append("'}).field({")
|
||||
.append("_id:true,")
|
||||
.append("masterActivityName:true,")
|
||||
.append("branchActivityName:true,")
|
||||
.append("pts:true,")
|
||||
.append("relationActivityId:true")
|
||||
.append("}).orderBy('createdAt','desc').limit(1000).get()");
|
||||
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString());
|
||||
if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return jsonObject.getJSONArray("data").stream()
|
||||
.map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按活动维度统计当前登录人在 sign 表中已打卡的点位数量。
|
||||
* 这里按 pts_code 去重后再计数,避免同一点位重复打卡时把活动误判成已完成。
|
||||
*/
|
||||
private Map<String, Integer> queryUserSignedPtsNumMap(String userId, List<FitnessWalkActivity> relationActivityList) {
|
||||
if (relationActivityList == null || relationActivityList.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
List<String> activityIds = relationActivityList.stream()
|
||||
.map(FitnessWalkActivity::get_id)
|
||||
.filter(Strings::isNotBlank)
|
||||
.collect(Collectors.toList());
|
||||
if (activityIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
StringBuilder sql = new StringBuilder("db.collection('sign').aggregate()");
|
||||
sql.append(".match({userid:'").append(userId).append("',activity_id:_.in(")
|
||||
.append(JSON.toJSONString(activityIds))
|
||||
.append(")})");
|
||||
sql.append(".group({_id:'$activity_id',signedPtsCodeSet:$.addToSet('$pts_code')})");
|
||||
sql.append(".project({_id:0,activityId:'$_id',signedPtsNum:$.size('$signedPtsCodeSet')})");
|
||||
sql.append(".end()");
|
||||
|
||||
JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, sql.toString());
|
||||
if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return jsonObject.getJSONArray("data").stream().map(item -> {
|
||||
JSONObject rowData = JSONObject.parseObject((String) item);
|
||||
String activityId = rowData.getString("activityId");
|
||||
int signedPtsNum = parseCloudNumber(rowData.get("signedPtsNum"));
|
||||
return NutMap.NEW().addv("activityId", activityId).addv("signedPtsNum", signedPtsNum);
|
||||
}).collect(Collectors.toMap(v -> v.getString("activityId"), v -> v.getInt("signedPtsNum")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个活动只有在存在点位,且当前用户已完成该活动全部点位打卡时,才算这个活动达标。
|
||||
*/
|
||||
private boolean isActivityCompleted(FitnessWalkActivity activity, Map<String, Integer> signedPtsNumMap) {
|
||||
int totalPtsNum = activity.getPts() == null ? 0 : activity.getPts().size();
|
||||
int signedPtsNum = signedPtsNumMap.getOrDefault(activity.get_id(), 0);
|
||||
if (signedPtsNum > totalPtsNum && totalPtsNum > 0) {
|
||||
signedPtsNum = totalPtsNum;
|
||||
}
|
||||
return totalPtsNum > 0 && signedPtsNum >= totalPtsNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 云函数聚合返回的数字字段可能是包装对象,这里统一兼容 Integer、Long 和 {$numberInt:xx} 结构。
|
||||
*/
|
||||
private int parseCloudNumber(Object value) {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
if (value instanceof JSONObject) {
|
||||
JSONObject jsonObject = (JSONObject) value;
|
||||
if (jsonObject.containsKey("$numberInt")) {
|
||||
return jsonObject.getIntValue("$numberInt");
|
||||
}
|
||||
if (jsonObject.containsKey("$numberLong")) {
|
||||
return jsonObject.getIntValue("$numberLong");
|
||||
}
|
||||
}
|
||||
return Integer.parseInt(String.valueOf(value));
|
||||
}
|
||||
}
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUnit;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkStep;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkStepManageService;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.vo.FitnessWalkDateStepVO;
|
||||
import com.budwk.app.zhgh.dayofficework.integral.model.IntegralDetail;
|
||||
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.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 活动步数与积分业务服务,集中处理查询、数据写入和导出。
|
||||
*/
|
||||
@IocBean
|
||||
public class FitnessWalkStepManageServiceImpl implements FitnessWalkStepManageService {
|
||||
|
||||
@Inject
|
||||
private FitnessWalkCommonService fitnessWalkCommonService;
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
/**
|
||||
* 合并微信运动最近30天步数,按每天最大步数保存并计算积分。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @param monthSteps 微信运动步数JSON数组,每项包含step及秒级timestamp
|
||||
* @return Result,code为0表示成功,data承载合并微信运动最近30天步数,按每天最大步数保存并计算积分结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result updateStepMonth(String activityId, String userId, String monthSteps) {
|
||||
try {
|
||||
if (StrUtil.isBlank(userId) || StrUtil.isBlank(monthSteps) || StrUtil.isBlank(activityId)) {
|
||||
return Result.error("必要参数未传递");
|
||||
}
|
||||
|
||||
// 健步走的活动数据
|
||||
FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId);
|
||||
// 是否启用了积分
|
||||
boolean isPoints = activity.getIsPoints() != null && activity.getIsPoints();
|
||||
|
||||
// 使用对象解构获取活动参数
|
||||
final Integer standSteps = activity.getLotteryQualificationSteps();
|
||||
final Integer standPoints = activity.getLotteryQualificationPoints();
|
||||
final Integer otherSteps = activity.getOtherSteps();
|
||||
final Integer otherPoints = activity.getOtherPoints();
|
||||
final Integer maxPoints = activity.getMaxPoints();
|
||||
|
||||
String integralNotes = activity.getMasterActivityName() + "所获积分";
|
||||
|
||||
// 获取到的用户的步数数据
|
||||
List<NutMap> steps = Json.fromJsonAsList(NutMap.class, monthSteps);
|
||||
|
||||
//判断数据是否完整 微信运动会在晚上10点多进行步数的更新 会导致31天前的步数获取变为0 所以这里去除掉第一个元素 即为31天前的数据 每次只保存最近30天的数据
|
||||
if (Lang.isNotEmpty(steps) && steps.size() == 31) {
|
||||
steps.remove(0);
|
||||
}
|
||||
|
||||
// 传递过来的步数数据
|
||||
List<FitnessWalkStep> wxSteps = steps.stream().map(v -> {
|
||||
FitnessWalkStep step = new FitnessWalkStep();
|
||||
step.setActivityId(activityId);
|
||||
step.setUserId(userId);
|
||||
step.setStep(v.getInt("step"));
|
||||
// 计算积分,判断有没有设置,超过达标步数后,每达标XX步数,获得XX积分
|
||||
int calculatedPoints = calculatePoints(isPoints, standSteps, standPoints, otherSteps, otherPoints, maxPoints, step.getStep());
|
||||
step.setPoints(calculatedPoints);
|
||||
DateTime date = DateUtil.parse(DateUtil.format(DateUtil.date(v.getLong("timestamp") * 1000), "yyyy-MM-dd"), "yyyy-MM-dd");
|
||||
step.setApplyDate(date);
|
||||
return step;
|
||||
}).toList();
|
||||
|
||||
// 传递过来的所有日期的集合
|
||||
List<Date> dateList = wxSteps.stream().map(FitnessWalkStep::getApplyDate).collect(Collectors.toList());
|
||||
|
||||
// 去数据库查询日期,这个玩意还要保证插入到数据库的单个日期只有一条,并且这个日期数据库之前有步数,传过来没步数那就要保留数据库的步数
|
||||
// 上面日期集合数据库的步数
|
||||
List<FitnessWalkStep> stepList = dao.query(FitnessWalkStep.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("userId", "=", userId).and("applyDate", "in", dateList));
|
||||
|
||||
// 这就是保留每天最大步数的集合,那这个集合里面applyDate就是唯一的了
|
||||
List<FitnessWalkStep> dupStepList = new ArrayList<>(stepList.stream()
|
||||
.collect(Collectors.toMap(FitnessWalkStep::getApplyDate, Function.identity(),
|
||||
BinaryOperator.maxBy(Comparator.comparing(FitnessWalkStep::getStep))))
|
||||
.values());
|
||||
|
||||
// 这是过滤的相同的applyDate的集合,这些数据要删除
|
||||
List<String> delStepIds = stepList.stream()
|
||||
.filter(step -> !dupStepList.contains(step))
|
||||
.map(FitnessWalkStep::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 数据库的数据 这个map就用于判断数据库的日期和传递过来日期的步数,两个是不是相等的,保留步数多的数据
|
||||
Map<Date, FitnessWalkStep> dateStepMap = dupStepList.stream().collect(Collectors.toMap(FitnessWalkStep::getApplyDate, v -> v));
|
||||
|
||||
List<FitnessWalkStep> resultStepList = new ArrayList<>();
|
||||
// 这里循环就是,循环的传递过来的数据
|
||||
for (FitnessWalkStep step : wxSteps) {
|
||||
// 数据库存储的步数
|
||||
FitnessWalkStep walkStep = dateStepMap.get(step.getApplyDate());
|
||||
// 如果数据库没有,那就要新增
|
||||
if (Lang.isEmpty(walkStep)) {
|
||||
resultStepList.add(step);
|
||||
} else {
|
||||
// 如果数据库有,那就要保留步数多的
|
||||
if (walkStep.getStep() < step.getStep()) {
|
||||
walkStep.setStep(step.getStep());
|
||||
resultStepList.add(walkStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 最后删除重复日期,更新步数数据
|
||||
if (Lang.isNotEmpty(delStepIds)) {
|
||||
dao.clear(FitnessWalkStep.class, Cnd.where("id", "in", delStepIds));
|
||||
if (standSteps != null && standSteps > 0) {
|
||||
// 删除积分明细
|
||||
dao.clear(IntegralDetail.class, Cnd.where("id", "in", delStepIds));
|
||||
}
|
||||
}
|
||||
if (Lang.isNotEmpty(resultStepList)) {
|
||||
dao.insertOrUpdate(resultStepList);
|
||||
if (standSteps != null && standSteps > 0) {
|
||||
// 增加积分明细
|
||||
int year = DateUtil.thisYear();
|
||||
List<IntegralDetail> integralDetailList = resultStepList.stream().map(v -> {
|
||||
IntegralDetail detail = new IntegralDetail();
|
||||
detail.setId(v.getId());
|
||||
detail.setYear(String.valueOf(year));
|
||||
detail.setUserId(userId);
|
||||
detail.setIntegral(String.valueOf(v.getPoints()));
|
||||
detail.setIntegralNotes(integralNotes);
|
||||
detail.setBizId(activityId);
|
||||
detail.setIntegralTime(v.getApplyDate());
|
||||
return detail;
|
||||
}).toList();
|
||||
|
||||
List<IntegralDetail> list = integralDetailList.stream().filter(v -> !"0".equals(v.getIntegral())).toList();
|
||||
if (Lang.isNotEmpty(list)) {
|
||||
dao.insertOrUpdate(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.success();
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
return Result.error("步数上传失败,请稍后重试,必要时请及时联系管理员");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询活动日期步数,返回step和毫秒级timestamp。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载查询活动日期步数,返回step和毫秒级timestamp结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result getActivityDateStep(String activityId, String userId) {
|
||||
try {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId);
|
||||
DateTime startTime = DateUtil.date(activity.getStartTime());
|
||||
DateTime endTime = DateUtil.date(activity.getEndTime());
|
||||
|
||||
cnd.and("activityId", "=", activityId);
|
||||
cnd.and("userId", "=", userId);
|
||||
cnd.and("applyDate", ">=", startTime);
|
||||
cnd.and("applyDate", "<=", endTime);
|
||||
cnd.asc("applyDate");
|
||||
cnd.groupBy("applyDate");
|
||||
List<FitnessWalkStep> stepList = baseService.dao().query(FitnessWalkStep.class, cnd);
|
||||
|
||||
List<FitnessWalkDateStepVO> list = stepList.stream().map(v -> {
|
||||
// 返回给小程序的折线图数据,timestamp 允许为空以兼容历史异常日期数据。
|
||||
Long timestamp = v.getApplyDate() == null ? null : v.getApplyDate().getTime();
|
||||
return new FitnessWalkDateStepVO(v.getStep(), timestamp);
|
||||
}).collect(Collectors.toList());
|
||||
return Result.success(list);
|
||||
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按每日或自定义规则计算用户达标步数与完成进度。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载按每日或自定义规则计算用户达标步数与完成进度结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result getActivityQualifyProgressBar(String activityId, String userId) {
|
||||
NutMap resultMap = NutMap.NEW();
|
||||
|
||||
FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId);
|
||||
//计步的抽奖模式 everyday custom
|
||||
String lotteryMode = activity.getLotteryMode();
|
||||
if ("everyday".equals(lotteryMode)) {
|
||||
FitnessWalkActivity.DayLotteryRule dayLotteryRule = activity.getDayLotteryRule();
|
||||
//每日抽奖资格步数
|
||||
Integer lotteryQualificationStep = dayLotteryRule.getLotteryQualificationStep();
|
||||
|
||||
FitnessWalkStep userWalkStep = baseService.dao().fetch(FitnessWalkStep.class, Cnd.where("userId", "=", userId)
|
||||
.and("DATE( applyDate )", "=", DateUtil.today()).and("activityId", "=", activityId));
|
||||
|
||||
int complianceDay = baseService.dao().count(FitnessWalkStep.class, Cnd.where("userId", "=", userId)
|
||||
.and("DATE( applyDate )", "=", DateUtil.today()).and("activityId", "=", activityId)
|
||||
.and("step", ">", lotteryQualificationStep));
|
||||
|
||||
Integer thisDayStep = userWalkStep.getStep();
|
||||
|
||||
BigDecimal lotteryQualificationStepBig = new BigDecimal(lotteryQualificationStep);
|
||||
BigDecimal thisDayStepBig = new BigDecimal(thisDayStep);
|
||||
//达标的小数,保留四位
|
||||
BigDecimal divide = thisDayStepBig.divide(lotteryQualificationStepBig, 4, RoundingMode.HALF_UP);
|
||||
BigDecimal percentValue = divide.multiply(new BigDecimal("100"));
|
||||
DecimalFormat decimalFormat = new DecimalFormat("#.##"); // 格式化为两位小数
|
||||
//百分数
|
||||
String formattedPercent = decimalFormat.format(percentValue);
|
||||
|
||||
long betweenDay = DateUtil.date(activity.getStartTime()).between(DateUtil.date(activity.getEndTime()), DateUnit.DAY);
|
||||
NutMap map = new NutMap();
|
||||
map.setv("mode", "everyDayLottery");
|
||||
//当前达标步数
|
||||
map.setv("userStep", thisDayStep);
|
||||
//达标总步数
|
||||
map.setv("complianceSumStep", lotteryQualificationStep);
|
||||
//达标比例
|
||||
map.setv("complianceProportion", Integer.parseInt(formattedPercent));
|
||||
//活动天数
|
||||
map.setv("betweenDay", betweenDay);
|
||||
//达标天数
|
||||
map.setv("complianceDay", complianceDay);
|
||||
resultMap.addv("everyDayLotteryMode", map);
|
||||
} else if ("custom".equals(lotteryMode)) {
|
||||
List<FitnessWalkActivity.CustomLotteryRule> customLotteryRules = activity.getCustomLotteryRules();
|
||||
List<FitnessWalkStep> userWalkSteps = baseService.dao().
|
||||
query(FitnessWalkStep.class, Cnd.where("userId", "=", userId)
|
||||
.and("activityId", "=", activityId)
|
||||
.and("DATE(applyDate)", ">=", DateUtil.format(DateUtil.date(activity.getStartTime()), "yyyy-MM-dd"))
|
||||
.and("DATE(applyDate)", "<=", DateUtil.format(DateUtil.date(activity.getEndTime()), "yyyy-MM-dd"))
|
||||
);
|
||||
|
||||
List<NutMap> result = new ArrayList<>();
|
||||
|
||||
for (FitnessWalkActivity.CustomLotteryRule rule : customLotteryRules) {
|
||||
NutMap nutMap = NutMap.NEW();
|
||||
|
||||
List<FitnessWalkStep> partSteps = userWalkSteps.stream()
|
||||
.filter(s -> (DateUtil.compare(s.getApplyDate(), rule.getStartDate()) >= 0 && DateUtil.compare(s.getApplyDate(), rule.getEndDate()) <= 0))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Integer lotteryQualificationDay = rule.getLotteryQualificationDay();
|
||||
Integer lotteryQualificationStep = rule.getLotteryQualificationStep();
|
||||
|
||||
if (lotteryQualificationDay != null && lotteryQualificationStep != null) {
|
||||
//每天步数
|
||||
nutMap.put("mode", "customLotteryEveryDayStepMode");
|
||||
//用户总达标步数
|
||||
nutMap.put("userSumStep", partSteps.stream().filter(s -> DateUtil.compare(s.getApplyDate(), new Date(), "yyyy-MM-dd") == 0).findFirst().map(s -> s.getStep()).orElse(0));
|
||||
//今天达标步数
|
||||
nutMap.put("complianceStep", lotteryQualificationStep);
|
||||
//总达标步数
|
||||
nutMap.put("complianceSumStep", lotteryQualificationDay * lotteryQualificationStep);
|
||||
//用户当前达标总天数
|
||||
nutMap.put("userComplianceSumDay", partSteps.stream().filter(s -> s.getStep() > lotteryQualificationStep).count());
|
||||
//总达标天数
|
||||
nutMap.put("complianceSumDay", lotteryQualificationDay);
|
||||
} else if (lotteryQualificationDay == null && lotteryQualificationStep != null) {
|
||||
//总步数
|
||||
nutMap.put("mode", "customLotterySumStepMode");
|
||||
//用户总达标步数
|
||||
nutMap.put("userSumStep", partSteps.stream().mapToInt(s -> s.getStep()).sum());
|
||||
//总达标步数
|
||||
nutMap.put("complianceSumStep", lotteryQualificationStep);
|
||||
|
||||
nutMap.put("title", DateUtil.format(rule.getStartDate(), "MM月dd日") + "至" + DateUtil.format(rule.getEndDate(), "MM月dd日"));
|
||||
|
||||
}
|
||||
result.add(nutMap);
|
||||
}
|
||||
resultMap.addv("customLotteryMode", result);
|
||||
}
|
||||
return Result.success(resultMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定用户在活动中的中奖记录。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param userId 登录接口返回的用户ID
|
||||
* @return Result,code为0表示成功,data承载查询指定用户在活动中的中奖记录结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result winningRecord(String activityId, String userId) {
|
||||
Sql sql = Sqls.create("""
|
||||
select awardName,startDate,endDate from fitness_walk_award_user where activityId=@activityId and userId=@userId and isRead=@isRead
|
||||
""").setParam("activityId",activityId).setParam("userId", userId).setParam("isRead",true);
|
||||
|
||||
/* List<FitnessWalkAwardUser> awardUsers = baseService.dao().query(FitnessWalkAwardUser.class,
|
||||
Cnd.where("activityId", "=", activityId)
|
||||
.and("userId", "=", userId).and("isRead", "=", true));*/
|
||||
return Result.success(baseService.listMap(sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记活动奖项已读,沿用源版活动维度更新范围。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @return Result,code为0表示成功,data承载标记活动奖项已读,沿用源版活动维度更新范围结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result hasReadAward(String activityId) {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据基础达标、额外步数和封顶规则计算单日积分。
|
||||
*
|
||||
* @param isPointsEnabled 源页面提交的isPointsEnabled业务参数
|
||||
* @param standSteps 源页面提交的standSteps业务参数
|
||||
* @param standPoints 源页面提交的standPoints业务参数
|
||||
* @param otherSteps 源页面提交的otherSteps业务参数
|
||||
* @param otherPoints 源页面提交的otherPoints业务参数
|
||||
* @param maxPoints 源页面提交的maxPoints业务参数
|
||||
* @param actualSteps 源页面提交的actualSteps业务参数
|
||||
* @return int,用于根据基础达标、额外步数和封顶规则计算单日积分
|
||||
*/
|
||||
private int calculatePoints(boolean isPointsEnabled,
|
||||
Integer standSteps, Integer standPoints,
|
||||
Integer otherSteps, Integer otherPoints,
|
||||
Integer maxPoints, int actualSteps) {
|
||||
// 基础条件检查
|
||||
if (!isPointsEnabled || standSteps == null || standPoints == null || actualSteps < standSteps) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 计算基础积分
|
||||
int totalPoints = standPoints;
|
||||
|
||||
// 计算额外积分
|
||||
if (hasExtraPoints(otherSteps, otherPoints)) {
|
||||
int extraSteps = actualSteps - standSteps;
|
||||
int extraPoints = (extraSteps / otherSteps) * otherPoints;
|
||||
totalPoints += extraPoints;
|
||||
}
|
||||
|
||||
// 应用积分上限
|
||||
if (hasMaxPointsLimit(maxPoints)) {
|
||||
return Math.min(totalPoints, maxPoints);
|
||||
}
|
||||
|
||||
return totalPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断额外步数和积分规则是否均配置为有效正数。
|
||||
*
|
||||
* @param otherSteps 源页面提交的otherSteps业务参数
|
||||
* @param otherPoints 源页面提交的otherPoints业务参数
|
||||
* @return boolean,用于判断额外步数和积分规则是否均配置为有效正数
|
||||
*/
|
||||
private boolean hasExtraPoints(Integer otherSteps, Integer otherPoints) {
|
||||
return otherSteps != null && otherPoints != null && otherSteps > 0 && otherPoints > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否配置了有效的单日积分上限。
|
||||
*
|
||||
* @param maxPoints 源页面提交的maxPoints业务参数
|
||||
* @return boolean,用于判断是否配置了有效的单日积分上限
|
||||
*/
|
||||
private boolean hasMaxPointsLimit(Integer maxPoints) {
|
||||
return maxPoints != null && maxPoints > 0;
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.service.impl;
|
||||
|
||||
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.budwk.app.base.dao.CndPlus;
|
||||
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.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkStepStatisticsService;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import java.util.*;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 计步明细统计业务服务,集中处理查询、数据写入和导出。
|
||||
*/
|
||||
@IocBean
|
||||
public class FitnessWalkStepStatisticsServiceImpl implements FitnessWalkStepStatisticsService {
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
|
||||
/**
|
||||
* 构建每日步数与用户单位信息的联查语句。
|
||||
*
|
||||
* @return String,用于构建每日步数与用户单位信息的联查语句
|
||||
*/
|
||||
private String getSql() {
|
||||
return """
|
||||
SELECT
|
||||
u.loginname,
|
||||
u.username,
|
||||
u.unionname,
|
||||
u.unitname,
|
||||
al.applyDate,
|
||||
al.step,
|
||||
date_format(al.applyDate,'%Y-%m-%d') as prizeData
|
||||
FROM
|
||||
`fitness_walk_step` al
|
||||
LEFT JOIN vw_user u ON u.id = al.userId
|
||||
$condition
|
||||
""";
|
||||
}
|
||||
|
||||
/**
|
||||
* 按活动、日期、工号及组织条件分页统计用户每日步数。
|
||||
*
|
||||
* @param pageForm 分页、查询字段和排序参数,页码从1开始
|
||||
* @param year 查询年度,四位年份
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param activityDate 起止日期数组,两项均为yyyy-MM-dd
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param unitId 单位ID,空值表示不追加单位筛选
|
||||
* @return Result,code为0表示成功,data承载按活动、日期、工号及组织条件分页统计用户每日步数结果,失败说明见msg
|
||||
*/
|
||||
@Override
|
||||
public Result pageData(PageForm pageForm,
|
||||
Integer year,
|
||||
String activityId,
|
||||
String[] activityDate,
|
||||
String unionId,
|
||||
String unitId) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
Sql sql = Sqls.create(getSql());
|
||||
if (Lang.isNotEmpty(activityDate)) {
|
||||
cnd.and("date_format(al.applyDate,'%Y-%m-%d')", ">=", activityDate[0]);
|
||||
cnd.and("date_format(al.applyDate,'%Y-%m-%d')", "<=", activityDate[1]);
|
||||
}
|
||||
if(StrUtil.isNotBlank(pageForm.getSearchKeyword())){
|
||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||
seg.orLike("u.username",pageForm.getSearchKeyword());
|
||||
seg.orLike("u.loginname",pageForm.getSearchKeyword());
|
||||
cnd.and(seg);
|
||||
}
|
||||
cnd.andEX("YEAR(al.applyDate)", "=", year);
|
||||
cnd.andEX("al.activityId", "=", activityId);
|
||||
cnd.andEX("u.unionid", "=", unionId);
|
||||
cnd.andEX("u.unitId", "=", unitId);
|
||||
cnd.desc("al.applyDate");
|
||||
cnd.desc("u.unioncode");
|
||||
cnd.groupBy("al.applyDate", "al.userId");
|
||||
|
||||
sql.setCondition(cnd);
|
||||
return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按当前活动和日期条件导出计步明细。
|
||||
*
|
||||
* @param activityId 健步走云端活动ID
|
||||
* @param activityDate 起止日期数组,两项均为yyyy-MM-dd
|
||||
* @param unionId 分工会ID,空值表示不追加筛选,已有数据权限仍生效
|
||||
* @param unitId 单位ID,空值表示不追加单位筛选
|
||||
* @param response HTTP响应,用于写入XSSF格式Excel文件流
|
||||
* 无返回对象,通过response写出Excel文件流。
|
||||
*/
|
||||
@Override
|
||||
@Ok("void")
|
||||
public void exportExcel(String activityId,
|
||||
String[] activityDate,
|
||||
String unionId,
|
||||
String unitId, HttpServletResponse response) {
|
||||
CndPlus cnd = CndPlus.create();
|
||||
Sql sql = Sqls.create(getSql());
|
||||
if (activityDate.length > 0) {
|
||||
cnd.and("date_format(al.applyDate,'%Y-%m-%d')", ">=", activityDate[0]);
|
||||
cnd.and("date_format(al.applyDate,'%Y-%m-%d')", "<=", activityDate[1]);
|
||||
}
|
||||
cnd.andEX("al.activityId", "=", activityId);
|
||||
cnd.andEX("u.unionid", "=", unionId);
|
||||
cnd.andEX("u.unitId", "=", unitId);
|
||||
cnd.desc("al.applyDate");
|
||||
cnd.desc("u.unioncode");
|
||||
sql.setCondition(cnd);
|
||||
|
||||
List<NutMap> exportList = baseService.listMap(sql);
|
||||
|
||||
List<ExcelExportEntity> entityList = new ArrayList<>() {{
|
||||
add(new ExcelExportEntity("姓名", "username", 20));
|
||||
add(new ExcelExportEntity("工号", "loginname", 20));
|
||||
add(new ExcelExportEntity("单位", "unitname", 20));
|
||||
add(new ExcelExportEntity("分工会", "unionname", 20));
|
||||
add(new ExcelExportEntity("当前步数", "step", 20));
|
||||
add(new ExcelExportEntity("日期", "prizeData", 20));
|
||||
}};
|
||||
|
||||
try {
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, exportList);
|
||||
CommonDownloadUtil.download("计步步数统计表.xlsx", workbook, response);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.budwk.app.zhgh.activity.fitnesswalk.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 小程序活动步数日期数据:step 表示当天步数,timestamp 表示 applyDate 对应的毫秒时间戳。
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FitnessWalkDateStepVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer step;
|
||||
|
||||
private Long timestamp;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.budwk.app.zhgh.map;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityService;
|
||||
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")
|
||||
@At("/platform/TMap")
|
||||
public class TMapController {
|
||||
@Inject
|
||||
private FitnessWalkActivityService fitnessWalkActivityService;
|
||||
|
||||
/**
|
||||
* 检索活动点位的候选地址。
|
||||
* @param keyword 用户输入的地名或地址关键字,不能为空
|
||||
* @return Result,data.status为腾讯地图状态,data.data为含title、address、location的候选列表
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result suggestion(String keyword) {
|
||||
if (StrUtil.isBlank(keyword)) {
|
||||
return Result.error("关键词不能为空");
|
||||
}
|
||||
return fitnessWalkActivityService.suggestMapAddress(keyword);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div style="position: relative !important">
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-autocomplete
|
||||
v-model="addressValue"
|
||||
style="width: 100%"
|
||||
:fetch-suggestions="querySearchAsync"
|
||||
placeholder="请输入关键词查询地址"
|
||||
@select="handleSelect"
|
||||
:trigger-on-focus="false"
|
||||
/>
|
||||
</div>
|
||||
<div id="mapContainer"></div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "TMap",
|
||||
props: {
|
||||
radius: {
|
||||
type: Number,
|
||||
default: 50
|
||||
},
|
||||
position: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
view: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
map: null,
|
||||
poi: this.position,
|
||||
appMapCenterPointX: 0,
|
||||
appMapCenterPointY: 0,
|
||||
markerLayer: null,
|
||||
addressValue: '',
|
||||
circle: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect(item) {
|
||||
if (item.location) {
|
||||
// 定位到中心点
|
||||
const center = new TMap.LatLng(item.location.lat, item.location.lng)
|
||||
this.map.setCenter(center)
|
||||
this.clearMarker()
|
||||
this.createMarker(center)
|
||||
this.$set(this, "poi", [item.location.lat, item.location.lng])
|
||||
}
|
||||
},
|
||||
// keyword为地址关键字;候选结果提供title、address和经纬度location。
|
||||
querySearchAsync(queryString, cb) {
|
||||
if (!queryString) {
|
||||
cb([])
|
||||
return
|
||||
}
|
||||
this.$axios.post("/platform/TMap/suggestion", {keyword: queryString}).then((res) => {
|
||||
if (res.code === 0 && res.data.status === 0) {
|
||||
cb((res.data.data || []).map((item) => Object.assign({}, item, {
|
||||
value: item.title + "【详细地址:" + item.address + "】"
|
||||
})))
|
||||
} else {
|
||||
this.$message.error(res.msg || res.data.message)
|
||||
cb([])
|
||||
}
|
||||
}).catch(() => {
|
||||
cb([])
|
||||
})
|
||||
},
|
||||
initMap() {
|
||||
const center = new TMap.LatLng(this.appMapCenterPointY, this.appMapCenterPointX)
|
||||
this.$set(this, "map", new TMap.Map('mapContainer', {
|
||||
resizeEnable: true,
|
||||
zoom: 16,
|
||||
center: center
|
||||
}))
|
||||
// 初始化
|
||||
this.$set(this, "markerLayer", new TMap.MultiMarker({
|
||||
map: this.map,
|
||||
geometries: []
|
||||
}));
|
||||
if (this.poi && this.poi.length > 0) {
|
||||
const posi = new TMap.LatLng(this.poi[0], this.poi[1])
|
||||
this.createMarker(posi)
|
||||
this.map.setCenter(posi)
|
||||
} else {
|
||||
this.createMarker(center)
|
||||
}
|
||||
this.map.on("click", (event) => {
|
||||
if (!this.view) {
|
||||
this.clearMarker()
|
||||
this.createMarker(event.latLng)
|
||||
this.$set(this, "poi", [event.latLng.getLat(), event.latLng.getLng()])
|
||||
}
|
||||
})
|
||||
},
|
||||
// 清除签到点位
|
||||
clearMarker() {
|
||||
if (this.markerLayer) {
|
||||
this.markerLayer.setGeometries([])
|
||||
}
|
||||
if(this.circle) {
|
||||
this.circle.setGeometries([])
|
||||
}
|
||||
},
|
||||
// 创建签到点位
|
||||
createMarker(position) {
|
||||
this.markerLayer.add([
|
||||
{
|
||||
id: 'marker_' + Date.now(),
|
||||
position: position
|
||||
}
|
||||
]);
|
||||
this.$set(this, "circle", new TMap.MultiCircle({
|
||||
map: this.map,
|
||||
geometries: [{
|
||||
center: position,
|
||||
radius: this.radius,
|
||||
}],
|
||||
}));
|
||||
},
|
||||
getConfigKey(key) {
|
||||
return this.$axios.post("/open/common/getConfigKey", {key}).then((resp) => resp.data)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
position(newVal) {
|
||||
this.$set(this, "poi", newVal)
|
||||
},
|
||||
poi(newVal) {
|
||||
this.$emit("update:position", newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 父页面已加载腾讯地图SDK;中心点沿用当前项目系统配置。
|
||||
Promise.all([this.getConfigKey("AppMapCenterPointX"), this.getConfigKey("AppMapCenterPointY")])
|
||||
.then(([lng, lat]) => {
|
||||
this.$set(this, "appMapCenterPointX", Number(lng))
|
||||
this.$set(this, "appMapCenterPointY", Number(lat))
|
||||
this.$nextTick(() => this.initMap())
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.map) {
|
||||
this.map.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#mapContainer {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,229 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<div class="search">
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">主活动名称:</div>
|
||||
<div class="search-item-option">
|
||||
<el-input v-model="pageForm.searchKeyword"
|
||||
clearable
|
||||
placeholder="请输入主活动名称"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-query">
|
||||
<el-button icon="el-icon-search" type="primary" @click="doSearch"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt10">
|
||||
<table-tool :app="this" label="关联项目列表">
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" @click="openAdd">创建关联项目</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table :data="tableData"
|
||||
:size="tableSize"
|
||||
class="vi-table"
|
||||
row-key="id"
|
||||
style="width: 100%"
|
||||
v-loading="tableLoading">
|
||||
<el-table-column type="index"
|
||||
label="序号"
|
||||
width="80px"
|
||||
align="center"
|
||||
header-align="center"
|
||||
:index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="masterActivityName"
|
||||
label="主活动名称"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="standardActivityNum"
|
||||
label="达标活动数量"
|
||||
align="center"
|
||||
header-align="center"></el-table-column>
|
||||
<el-table-column label="创建时间"
|
||||
align="center"
|
||||
header-align="center">
|
||||
<template scope="{row}">
|
||||
{{row.createdAt ? $moment(row.createdAt).format('YYYY-MM-DD HH:mm:ss') : ''}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作"
|
||||
width="240px"
|
||||
align="center"
|
||||
header-align="center">
|
||||
<template scope="{row}">
|
||||
<el-button type="success" size="mini" @click="openView(row)">查看</el-button>
|
||||
<el-button type="primary" size="mini" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button type="danger" size="mini" @click="doDelete(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-row class="el-pagination-container">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageForm.totalCount">
|
||||
</el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<template #edit>
|
||||
<el-form :model="formData" :rules="rules" label-width="120px" ref="form">
|
||||
<el-form-item label="主活动名称" prop="masterActivityName">
|
||||
<el-input v-model="formData.masterActivityName" maxlength="100" placeholder="请填写主活动名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="达标活动数量" prop="standardActivityNum">
|
||||
<el-input-number v-model="formData.standardActivityNum"
|
||||
:min="1"
|
||||
:precision="0"
|
||||
style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="display:flex;justify-content:flex-end">
|
||||
<el-button type="primary" @click="doSubmit" :loading="formLoading">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #view>
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="主活动名称">
|
||||
{{viewData.masterActivityName}}
|
||||
</el-form-item>
|
||||
<el-form-item label="达标活动数量">
|
||||
{{viewData.standardActivityNum}}
|
||||
</el-form-item>
|
||||
<el-form-item label="关联活动数">
|
||||
{{viewActivityList.length}}
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="viewActivityList" border style="width:100%">
|
||||
<el-table-column prop="activityName"
|
||||
label="活动名称"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="activityModelName"
|
||||
label="活动模式"
|
||||
width="120"
|
||||
align="center"
|
||||
header-align="center"></el-table-column>
|
||||
<el-table-column label="创建时间"
|
||||
width="180"
|
||||
align="center"
|
||||
header-align="center">
|
||||
<template scope="{row}">
|
||||
{{row.createdAt ? $moment(row.createdAt).format('YYYY-MM-DD HH:mm:ss') : ''}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
viewData: {},
|
||||
viewActivityList: [],
|
||||
rules: {
|
||||
masterActivityName: [{required: true, message: '请填写主活动名称', trigger: ['change', 'blur']}],
|
||||
standardActivityNum: [{required: true, message: '请填写达标活动数量', trigger: ['change', 'blur']}]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openView(row) {
|
||||
this.$axios.post(loc() + '/relationActivityList', {id: row.id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$set(this, "viewData", Object.assign({}, row))
|
||||
this.$set(this, "viewActivityList", resp.data || [])
|
||||
this.$refs.guava.view()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
openAdd() {
|
||||
this.$refs.guava.edit()
|
||||
this.$nextTick(() => {
|
||||
this.$set(this, "formData", {
|
||||
standardActivityNum: 1
|
||||
})
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
openEdit(row) {
|
||||
this.$refs.guava.edit()
|
||||
this.$nextTick(() => {
|
||||
this.$set(this, "formData", Object.assign({}, row))
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
this.$set(this, "formLoading", true)
|
||||
this.$axios.post(loc() + '/doSave', this.formData).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.notifySuccess(resp.msg)
|
||||
this.$refs.guava.index()
|
||||
this.pageData()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, "formLoading", false)
|
||||
})
|
||||
})
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm('您确定要删除该关联项目吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$set(this, "formLoading", true)
|
||||
this.$axios.post(loc() + '/doRemove', {id: id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.notifySuccess(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, "formLoading", false)
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,281 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<div class="search">
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">年份:</div>
|
||||
<div class="search-item-option">
|
||||
<el-date-picker
|
||||
:clearable="false"
|
||||
placeholder="选择年"
|
||||
style="width: 100%"
|
||||
@change="getCascadersActivity"
|
||||
type="year"
|
||||
v-model="pageForm.year"
|
||||
value-format="yyyy">
|
||||
</el-date-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">活动:</div>
|
||||
<div class="search-item-option">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<el-select
|
||||
:style="{width: subActivityOptions.length > 0 ? '50%' : '100%'}"
|
||||
placeholder="请选择主活动"
|
||||
@change="mainActivityChange"
|
||||
v-model="pageForm.mainActivityId"
|
||||
clearable>
|
||||
<el-option
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-for="item in activityOptions"></el-option>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="subActivityOptions.length > 0"
|
||||
style="width: 50%"
|
||||
placeholder="请选择分活动"
|
||||
@change="subActivityChange"
|
||||
v-model="pageForm.subActivityId"
|
||||
clearable>
|
||||
<el-option
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-for="item in subActivityOptions"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">分工会:</div>
|
||||
<div class="search-item-option">
|
||||
<el-select
|
||||
@change="doSearch"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择分工会"
|
||||
style="width: 100%"
|
||||
v-model="pageForm.unionId">
|
||||
<el-option
|
||||
:key="item.id"
|
||||
:label="item.unionname"
|
||||
:value="item.id"
|
||||
v-for="item in unionOptions"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">姓名/工号:</div>
|
||||
<div class="search-item-option">
|
||||
<el-input
|
||||
@keyup.enter.native="doSearch"
|
||||
clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
v-model="pageForm.searchKeyword"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-query">
|
||||
<el-button @click="doSearch" icon="el-icon-search" type="primary"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="人员打卡情况">
|
||||
<el-radio-group
|
||||
v-model="pageForm.qualifiedStatus"
|
||||
@change="doSearch"
|
||||
size="mini">
|
||||
<el-radio-button label="qualified">已达标人员</el-radio-button>
|
||||
<el-radio-button label="unqualified">未达标人员</el-radio-button>
|
||||
<el-radio-button label="all">全部</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-button
|
||||
@click="exportExcel"
|
||||
class="ml10"
|
||||
icon="el-icon-download"
|
||||
size="small"
|
||||
type="primary">
|
||||
导出
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :size="tableSize" v-loading="tableLoading">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index"
|
||||
width="80px"></el-table-column>
|
||||
<el-table-column
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:key="column.prop"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
let vue = new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
tableColumns: [
|
||||
{label: '姓名', prop: 'username'},
|
||||
{label: '工号', prop: 'loginname'},
|
||||
{label: '分工会', prop: 'unionname'},
|
||||
{label: '单位', prop: 'unitname'},
|
||||
{label: '达标几个活动', prop: 'qualifiedActivityNum', sortable: true},
|
||||
{label: '点位总数', prop: 'pts_size', sortable: true},
|
||||
{label: '已打卡点位数', prop: 'sign_size', sortable: true},
|
||||
{label: '是否全部打卡完全部点位', prop: 'isAllSigned', sortable: true}
|
||||
],
|
||||
unionOptions: [],
|
||||
pageForm: {
|
||||
unionId: '',
|
||||
year: new Date().getFullYear() + '',
|
||||
mainActivityId: '',
|
||||
subActivityId: '',
|
||||
activityId: '',
|
||||
qualifiedStatus: 'all',
|
||||
searchKeyword: '',
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0
|
||||
},
|
||||
activityOptions: [],
|
||||
subActivityOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 根据当前页面选择,统一返回活动查询参数。
|
||||
* 后端会按主活动、分活动与最终 activityId 自动判断查询单个活动还是整个关联活动集合。
|
||||
*/
|
||||
buildActivityRequestParams() {
|
||||
return {
|
||||
mainActivityId: this.pageForm.mainActivityId,
|
||||
subActivityId: this.pageForm.subActivityId,
|
||||
activityId: this.pageForm.activityId,
|
||||
qualifiedStatus: this.pageForm.qualifiedStatus
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 导出与列表复用同一套筛选条件,保证当前可见条件与导出结果一致。
|
||||
*/
|
||||
buildExportQueryString() {
|
||||
const params = Object.assign({
|
||||
unionId: this.pageForm.unionId,
|
||||
searchKeyword: this.pageForm.searchKeyword
|
||||
}, this.buildActivityRequestParams())
|
||||
return 'mainActivityId=' + encodeURIComponent(params.mainActivityId || '')
|
||||
+ '&subActivityId=' + encodeURIComponent(params.subActivityId || '')
|
||||
+ '&activityId=' + encodeURIComponent(params.activityId || '')
|
||||
+ '&unionId=' + encodeURIComponent(params.unionId || '')
|
||||
+ '&searchKeyword=' + encodeURIComponent(params.searchKeyword || '')
|
||||
+ '&qualifiedStatus=' + encodeURIComponent(params.qualifiedStatus || 'all')
|
||||
},
|
||||
exportExcel() {
|
||||
window.open(loc() + '/exportExcel?' + this.buildExportQueryString())
|
||||
},
|
||||
/**
|
||||
* 根据主活动构建分活动选项,并同步当前真正查询用的 activityId。
|
||||
* 当主活动存在分活动时,默认保持“只选主活动”的聚合查询,用户手动选择分活动后再切到单活动查询。
|
||||
*/
|
||||
syncActivitySelection(mainActivityId, needSearch) {
|
||||
const currentMainActivity = this.activityOptions.find(item => {
|
||||
return item.value === mainActivityId
|
||||
})
|
||||
const children = currentMainActivity && currentMainActivity.children ? currentMainActivity.children : []
|
||||
this.$set(this, 'subActivityOptions', children)
|
||||
|
||||
if (!currentMainActivity) {
|
||||
this.$set(this.pageForm, 'mainActivityId', '')
|
||||
this.$set(this.pageForm, 'subActivityId', '')
|
||||
this.$set(this.pageForm, 'activityId', '')
|
||||
this.$set(this, 'tableData', [])
|
||||
this.$set(this.pageForm, 'totalCount', 0)
|
||||
return
|
||||
}
|
||||
|
||||
if (children.length > 0) {
|
||||
const currentSubActivityId = this.pageForm.subActivityId
|
||||
const hasMatchedSubActivity = currentSubActivityId && children.some(item => {
|
||||
return item.value === currentSubActivityId
|
||||
})
|
||||
this.$set(this.pageForm, 'subActivityId', hasMatchedSubActivity ? currentSubActivityId : '')
|
||||
this.$set(this.pageForm, 'activityId', hasMatchedSubActivity ? currentSubActivityId : mainActivityId)
|
||||
} else {
|
||||
this.$set(this.pageForm, 'subActivityId', '')
|
||||
this.$set(this.pageForm, 'activityId', mainActivityId)
|
||||
}
|
||||
|
||||
if (needSearch) {
|
||||
this.doSearch()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 主活动切换后,重新联动分活动并立即刷新列表。
|
||||
*/
|
||||
mainActivityChange(val) {
|
||||
this.$set(this.pageForm, 'mainActivityId', val || '')
|
||||
this.$set(this.pageForm, 'subActivityId', '')
|
||||
this.syncActivitySelection(this.pageForm.mainActivityId, true)
|
||||
},
|
||||
/**
|
||||
* 分活动切换后,更新最终查询 activityId 并刷新列表。
|
||||
*/
|
||||
subActivityChange(val) {
|
||||
this.$set(this.pageForm, 'subActivityId', val || '')
|
||||
this.$set(this.pageForm, 'activityId', val || this.pageForm.mainActivityId || '')
|
||||
this.doSearch()
|
||||
},
|
||||
|
||||
/**
|
||||
* 年份切换后重载活动级联数据,并默认选中首个可用活动。
|
||||
*/
|
||||
getCascadersActivity() {
|
||||
this.$axios.post(loc() + '/getCascadersActivity', {year: this.pageForm.year}).then(res => {
|
||||
if (res.code !== 0) {
|
||||
this.$message.error(res.msg)
|
||||
return
|
||||
}
|
||||
this.$set(this, 'activityOptions', res.data || [])
|
||||
if (this.activityOptions.length > 0) {
|
||||
this.$set(this.pageForm, 'mainActivityId', this.activityOptions[0].value)
|
||||
this.$set(this.pageForm, 'subActivityId', '')
|
||||
this.syncActivitySelection(this.pageForm.mainActivityId, false)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$set(this, 'subActivityOptions', [])
|
||||
this.$set(this.pageForm, 'mainActivityId', '')
|
||||
this.$set(this.pageForm, 'subActivityId', '')
|
||||
this.$set(this.pageForm, 'activityId', '')
|
||||
this.$set(this, 'tableData', [])
|
||||
this.$set(this.pageForm, 'totalCount', 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$businessTool.listUnion().then(res => {
|
||||
this.$set(this, 'unionOptions', res || [])
|
||||
this.getCascadersActivity()
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user