diff --git a/src/com/engine/recruit/conn/ApplicantCommonInfo.java b/src/com/engine/recruit/conn/ApplicantCommonInfo.java index ed91d39..6bec8ea 100644 --- a/src/com/engine/recruit/conn/ApplicantCommonInfo.java +++ b/src/com/engine/recruit/conn/ApplicantCommonInfo.java @@ -288,4 +288,37 @@ public class ApplicantCommonInfo { } return -1; } + + /** + * 根据customSearchCode获取查询列表的id + * + * @param customSearchCode customSearchCode + * @return ID + */ + public static String getCustomSearchId(String customSearchCode) { + RecordSet rs = new RecordSet(); + rs.executeQuery("select id from mode_customsearch where customsearchcode = ? ", customSearchCode); + if (rs.next()) { + return rs.getString("id"); + } + return ""; + } + + /** + * 根据customSearchCode获取查询列表的地址 + * + * @param customSearchCode customSearchCode + * @param linkUrl 自定义的URL + * @return 查询列表的地址 + */ + public static String getCustomSearchLink(String customSearchCode, String linkUrl) { + if (StringUtils.isNotBlank(linkUrl)) { + return linkUrl; + } + String customSearchId = getCustomSearchId(customSearchCode); + if (StringUtils.isNotBlank(customSearchId)) { + return "/spa/cube/index.html#/main/cube/search?customid=" + customSearchId; + } + return ""; + } } diff --git a/src/com/engine/recruit/constant/RecruitConstant.java b/src/com/engine/recruit/constant/RecruitConstant.java index c2e0391..04c4c36 100644 --- a/src/com/engine/recruit/constant/RecruitConstant.java +++ b/src/com/engine/recruit/constant/RecruitConstant.java @@ -36,6 +36,7 @@ public class RecruitConstant { public static final String OFFER_MOBILE_URL; public static final String APPLICANTS_RESUMES_CATEGORY; public static final String INTERVIEW_FEEDBACK_URL; + public static final String REMIND_SEARCH_LINK; public static final String OCR_TYPE; public static final String OCR_URL; public static final String APP_ID; @@ -65,7 +66,8 @@ public class RecruitConstant { APPLICANTS_RESUMES_CATEGORY = getRecruitPropValue("APPLICANTS_RESUMES_CATEGORY"); // 面试反馈地址 INTERVIEW_FEEDBACK_URL = getCompleteUrl(getRecruitPropValue("INTERVIEW_FEEDBACK_URL")); - + // 简历订阅相关 + REMIND_SEARCH_LINK = getCompleteUrl(getRecruitPropValue("REMIND_SEARCH_LINK")); // OCR相关 OCR_TYPE = getRecruitPropValue("OCR_TYPE"); OCR_URL = getRecruitPropValue("OCR_URL"); diff --git a/src/weaver/formmode/recruit/customsearch/RemindSearchTemplate.java b/src/weaver/formmode/recruit/customsearch/RemindSearchTemplate.java new file mode 100644 index 0000000..47c387f --- /dev/null +++ b/src/weaver/formmode/recruit/customsearch/RemindSearchTemplate.java @@ -0,0 +1,29 @@ +package weaver.formmode.recruit.customsearch; + +import org.apache.commons.lang3.StringUtils; +import weaver.formmode.customjavacode.AbstractCustomSqlConditionJavaCode; +import weaver.general.Util; +import weaver.hrm.User; + +import java.util.Map; + +/** + * 简历订阅、推送记录列表查询条件 + * + * @author:dxfeng + * @createTime: 2024/03/11 + * @version: 1.0 + */ +public class RemindSearchTemplate extends AbstractCustomSqlConditionJavaCode { + @Override + public String generateSqlCondition(Map param) { + String whereSql = ""; + User user = (User) param.get("user"); + whereSql += " t1.jsr = '" + user.getUID() + "'"; + String uuid = Util.null2String(param.get("uuid")); + if (StringUtils.isNotBlank(uuid)) { + whereSql += " and t1.uuid = '" + uuid + "'"; + } + return whereSql; + } +} diff --git a/src/weaver/interfaces/recruit/cronjob/ResumePushJob.java b/src/weaver/interfaces/recruit/cronjob/ResumePushJob.java new file mode 100644 index 0000000..5e53667 --- /dev/null +++ b/src/weaver/interfaces/recruit/cronjob/ResumePushJob.java @@ -0,0 +1,484 @@ +package weaver.interfaces.recruit.cronjob; + +import cn.hutool.core.convert.Convert; +import com.api.cube.util.CubeCipherUitl; +import com.engine.recruit.conn.ApplicantCommonInfo; +import com.engine.recruit.conn.RecruitDataMap; +import com.engine.recruit.conn.RecruitRecordSet; +import com.engine.recruit.constant.RecruitConstant; +import com.engine.recruit.exception.CustomizeRunTimeException; +import org.apache.commons.lang3.StringUtils; +import weaver.common.DateUtil; +import weaver.conn.RecordSet; +import weaver.formmode.recruit.modeexpand.util.RecruitModeUtil; +import weaver.formmode.service.RemindJobService; +import weaver.formmode.task.TaskService; +import weaver.formmode.virtualform.VirtualFormHandler; +import weaver.general.BaseBean; +import weaver.general.Util; +import weaver.hrm.resource.ResourceComInfo; +import weaver.interfaces.schedule.BaseCronJob; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author:dxfeng + * @createTime: 2024/03/07 + * @version: 1.0 + */ +public class ResumePushJob extends BaseCronJob { + private final RemindJobService remindJobService = new RemindJobService(); + private final String remindHistoryTable = "uf_jcl_jldy"; + + private String currentDate; + private String uuid; + private Map> checkRepeatMap; + private int remindHistoryModeId; + + @Override + public void execute() { + BaseBean baseBean = new BaseBean(); + baseBean.writeLog("简历订阅推送任务开始..."); + currentDate = DateUtil.getFullDate(); + uuid = UUID.randomUUID().toString(); + checkRepeatMap = new HashMap<>(); + + Calendar calendar = Calendar.getInstance(Locale.CHINA); + Calendar limitDate = DateUtil.addMonth(calendar, -1); + Date time = limitDate.getTime(); + String date = DateUtil.getDate(time); + RecordSet rs = new RecordSet(); + // 推送规则,针对一条简历订阅,首次设置订阅规则且启用的,筛选投递时间为一个月内的简历(简历表),后续为增量推送 + String sql = "select t1.* from uf_jcl_yppc t1 where t1.tdsj > '" + date + "' "; + + remindHistoryModeId = ApplicantCommonInfo.getModeIdByTableName(remindHistoryTable); + // 建模ID + int modeId = ApplicantCommonInfo.getModeIdByTableName("uf_jcl_yppc"); + // 简历订阅规则设置,页面拓展ID + String pageExpand = ""; + rs.executeQuery("select id from mode_pageexpand where modeid = ? and expendname = ?", modeId, "简历订阅规则设置"); + if (rs.next()) { + pageExpand = rs.getString("id"); + } + if (StringUtils.isBlank(pageExpand)) { + throw new CustomizeRunTimeException("未获取到对应的页面拓展配置信息"); + } + + // 查询所有的消息提醒类型的页面拓展 + String taskSql = "select id from mode_timedtask_detail where isenable='1' and id in (select taskdetailid from mode_pageexpanddetail where interfacetype='5' and mainid=?)"; + + rs.executeQuery(taskSql, pageExpand); + List> taskDetailList = RecruitRecordSet.getRecordMapList(rs); + Map remindCountJob = new ConcurrentHashMap<>(); + + baseBean.writeLog("简历订阅推送任务生成列表数据..."); + for (Map map : taskDetailList) { + String taskDetailId = Util.null2String(map.get("id")); + Map taskDetailMap = remindJobService.getTaskDetailById(Convert.toInt(taskDetailId)); + String conditionsType = Util.null2String(taskDetailMap.get("conditionstype")); + String whereSql = ""; + if ("1".equals(conditionsType)) { + whereSql = Util.null2String(taskDetailMap.get("conditionsfield")); + } else if ("2".equals(conditionsType)) { + whereSql = Util.null2String(taskDetailMap.get("conditionssql")); + } + rs.executeQuery(sql + (StringUtils.isNotBlank(whereSql) ? " and (" + whereSql + ")" : "")); + List> recordMapList = RecruitRecordSet.getRecordMapList(rs); + for (Map recordMap : recordMapList) { + String billId = Util.null2String(recordMap.get("id")); + // 判断当前规则,有无推送过这条数据,同一规则,不重复推送数据 + boolean push = isPush(billId, taskDetailId); + if (!push) { + continue; + } + // 该规则推送的人员ID集合 + Set remindUserList = getRemindUserSet(taskDetailMap, billId); + // 统计每个人推送多少条数据 + for (Integer remindUserId : remindUserList) { + // 插入订阅历史表数据 + insertPushHistory(recordMap, remindUserId, remindCountJob); + } + + } + + + } + // 简历订阅列表URL + String remindSearchLink = ApplicantCommonInfo.getCustomSearchLink("df6f9744792840648c0c49991edb5112", RecruitConstant.REMIND_SEARCH_LINK); + if (StringUtils.isNotBlank(remindSearchLink)) { + remindSearchLink += "&uuid=" + uuid; + } + + baseBean.writeLog("简历订阅推送任务发送消息提醒..."); + // 给每个人推送当前规则的代办 + for (Map.Entry entry : remindCountJob.entrySet()) { + Integer key = entry.getKey(); + Integer value = entry.getValue(); + String remindContent = "有{remindCount}条简历推送给您,请查阅。"; + String content = remindContent.replace("remindCount", value.toString()); + // 发送代办 + String remindTitle = "简历订阅推送提醒"; + Set userIds = new HashSet<>(1); + userIds.add(Integer.toString(key)); + RecruitModeUtil.messagePush(RecruitConstant.RECRUIT_MESSAGE_TYPE, remindTitle, content, userIds, 1, remindSearchLink, ""); + } + baseBean.writeLog("简历订阅推送任务结束..."); + + } + + + /** + * 插入订阅历史表数据 + * + * @param recordMap + * @param remindUserId + * @param remindCountJob + */ + private void insertPushHistory(Map recordMap, Integer remindUserId, Map remindCountJob) { + if (recordMap == null || recordMap.isEmpty()) { + return; + } + String billId = Util.null2String(recordMap.get("id")); + Set hashSet = new HashSet<>(); + if (checkRepeatMap.containsKey(remindUserId)) { + Set containsSet = checkRepeatMap.get(remindUserId); + if (containsSet.contains(billId)) { + return; + } + hashSet.addAll(containsSet); + } + hashSet.add(billId); + checkRepeatMap.put(remindUserId, hashSet); + + RecruitDataMap insertDataMap = new RecruitDataMap<>(); + String modeUuid = UUID.randomUUID().toString(); + insertDataMap.put("modeuuid", modeUuid); + insertDataMap.put("formmodeid", remindHistoryModeId); + RecruitRecordSet.buildModeInsertFields(insertDataMap, 1); + // 姓名 + insertDataMap.put("xm", billId); + // 最近投递职位 + insertDataMap.put("zjtdzw", recordMap.get("ypzw")); + // 性别 + insertDataMap.put("xb", recordMap.get("xb")); + // 最高学历 + insertDataMap.put("zgxl", recordMap.get("zgxl")); + // 工作经验 + insertDataMap.put("gzjy", recordMap.get("gzjy")); + // 简历来源 + insertDataMap.put("jlly", recordMap.get("jlly")); + // 推送时间 + insertDataMap.put("tssj", currentDate); + // 接收人 + insertDataMap.put("jsr", remindUserId); + // UUID + insertDataMap.put("uuid", uuid); + // 插入推送历史表 + RecruitRecordSet.insertData(insertDataMap, remindHistoryTable); + // 权限重构 + int id = RecruitRecordSet.refreshRight(modeUuid, remindHistoryTable, remindHistoryModeId, 1); + + // 统计每个人推送多少条数据 + if (id != -1) { + remindCountJob.merge(remindUserId, 1, Integer::sum); + } + } + + + /** + * 获取当前数据,配置的提醒对象的ID集合 + * + * @param remindJob + * @param billid + * @return + */ + private Set getRemindUserSet(Map remindJob, String billid) { + String formtype = Util.null2String(remindJob.get("formtype")); + //字段id + String id = Util.null2String(remindJob.get("id")); + String modeid = Util.null2String(remindJob.get("modeid")); + //表单id + String formid = Util.null2String(remindJob.get("formid")); + + + TaskService taskService = new TaskService(); + RecordSet rs = new RecordSet(); + RecordSet rs2 = new RecordSet(); + RecordSet rs3 = new RecordSet(); + RecordSet rs4 = new RecordSet(); + + + String tablename = ""; + String sqlStr = "select b.tablename from modeinfo a,workflow_bill b where a.id=" + modeid + " and a.formid=b.id"; + rs.executeQuery(sqlStr); + if (rs.next()) { + tablename = rs.getString("tablename"); + } + // 判断是否为 虚拟表单; + boolean isvirtualform = VirtualFormHandler.isVirtualForm(formid); + String vdatasource = ""; + String vprimarykey = ""; + if (isvirtualform) { + Map vFormInfo = VirtualFormHandler.getVFormInfo(formid); + vdatasource = Util.null2String(vFormInfo.get("vdatasource")); + vprimarykey = Util.null2String(vFormInfo.get("vprimarykey")); + tablename = VirtualFormHandler.getRealFromName(tablename); + String vformtype = Util.null2String(vFormInfo.get("vformtype")); + String vsql = Util.null2String(vFormInfo.get("vsql")); + if ("2".equals(vformtype)) { + tablename = "(" + vsql + ")"; + } + } + + String sql; + if (!"0".equals(formtype) && !"".equals(formtype)) { + sql = "select t1.*,d1.id as subbillid from " + tablename + " t1 left join " + tablename + "_dt" + formtype + " d1 on t1.id = d1.mainid where t1.id=" + billid; + } else { + sql = " select * from " + tablename + " t1 where t1.id=" + billid; + if (isvirtualform) { + sql = " select * from " + tablename + " t1 where t1." + vprimarykey + "='" + billid + "'"; + } + } + //检验条件 + String sqlwhere = ""; + String conditionstype = Util.null2String(remindJob.get("conditionstype")); + if ("1".equals(conditionstype)) { + //字段 + String conditionsfield = Util.null2String(remindJob.get("conditionsfield")); + if (!"".equals(conditionsfield)) { + sqlwhere = " and (" + conditionsfield + ") "; + } + } else if ("2".equals(conditionstype)) { + //sql + String conditionssql = Util.null2String(remindJob.get("conditionssql")); + if (!"".equals(conditionssql)) { + sqlwhere = " and (" + conditionssql + ") "; + } + } + if (!"".equals(sqlwhere)) { + sql = sql + sqlwhere; + } + rs.isReturnDecryptData(true); + if (isvirtualform) { + rs.executeQuery(sql, vdatasource); + } else { + rs.executeQuery(sql); + } + + Set userSet = new HashSet<>(); + String remind_rule_sql = "select * from remind_multi_ruleinfo where taskdetailid='" + id + "' order by receivertype,id "; + while (rs.next()) { + String subbillid = rs.getString("subbillid"); + List userList = new ArrayList<>(); + rs3.execute(remind_rule_sql); + while (rs3.next()) { + String receivertype = rs3.getString("receivertype"); + String receiverdetail = rs3.getString("receiverdetail"); + String receiverlevel = rs3.getString("showlevel"); + String receiverlevel2 = rs3.getString("showlevel2"); + String receiverfieldtype = rs3.getString("receiverfieldtype"); + // 上级关系 1 当前人员, 2 直接上级, 3 所有上级, 4 本部门 ,5 本分部, 6 本岗位 + int remindhigherlevel = Util.getIntValue(rs3.getString("higherlevel"), 1); + //1 :所有上级 2:所有下级 + int orgrelation = Util.getIntValue(rs3.getString("orgrelation"), 1); + //----------------处理提醒人员------------------- + if ("1".equals(receivertype)) { + //人力资源 + userList = taskService.getRemind_ruleUserList(userList, Util.getIntValue(receivertype), receiverdetail, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if ("2".equals(receivertype)) { + //分部 + userList = taskService.getRemind_ruleUserList(userList, Util.getIntValue(receivertype), receiverdetail, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if ("3".equals(receivertype)) { + //部门 + userList = taskService.getRemind_ruleUserList(userList, Util.getIntValue(receivertype), receiverdetail, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if ("4".equals(receivertype)) { + //角色 + userList = taskService.getRemind_ruleUserList(userList, Util.getIntValue(receivertype), receiverdetail, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if ("5".equals(receivertype)) { + //所有人 + userList = taskService.getRemind_ruleUserList(userList, Util.getIntValue(receivertype), receiverdetail, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if ("6".equals(receivertype)) { + //模块创建人 + int modedatacreater = rs.getInt("modedatacreater"); + //考虑上级关系 1 当前人员, 2 直接上级, 3 所有上级, 4 本部门 ,5 本分部, 6 本岗位 remindhigherlevel + if (remindhigherlevel == 1) { + // 1 当前人员 + userList.add(modedatacreater); + } else if (remindhigherlevel == 2) { + // 2 直接上级 + try { + ResourceComInfo rc = new ResourceComInfo(); + int manager = Util.getIntValue(rc.getManagerID("" + modedatacreater), 0); + userList.add(manager); + } catch (Exception e) { + e.printStackTrace(); + } + + } else if (remindhigherlevel == 3) { + // 3 所有上级 + try { + ResourceComInfo rc = new ResourceComInfo(); + String managers = rc.getAllManagerByUserId("" + modedatacreater); + String[] ms = managers.split(","); + for (String m : ms) { + userList.add(Integer.parseInt(m)); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } else if (remindhigherlevel == 4) { + // 4 本部门 + // 考虑安全级别 + try { + ResourceComInfo rc = new ResourceComInfo(); + String departmentID = rc.getDepartmentID("" + modedatacreater); + userList = taskService.getRemind_ruleUserList(userList, 3, departmentID, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } catch (Exception e) { + e.printStackTrace(); + } + + } else if (remindhigherlevel == 5) { + // 5 本分部 + // 考虑安全级别 + try { + ResourceComInfo rc = new ResourceComInfo(); + String subCompanyID = rc.getSubCompanyID("" + modedatacreater); + userList = taskService.getRemind_ruleUserList(userList, 2, subCompanyID, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } catch (Exception e) { + e.printStackTrace(); + } + + } else if (remindhigherlevel == 6) { + // 6 本岗位 + try { + ResourceComInfo rc = new ResourceComInfo(); + String jobTitle = rc.getJobTitle("" + modedatacreater); + userList = taskService.getRemind_ruleUserList(userList, 6, jobTitle, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } else if ("1000".equals(receivertype)) { + //字段 + String objids = ""; + String sqlstr1 = "select id,fieldname,detailtable,viewtype from workflow_billfield where id in (" + receiverdetail + ")"; + rs4.executeQuery(sqlstr1); + String tempFieldName; + String viewtype; + while (rs4.next()) { + tempFieldName = rs4.getString("fieldname").toLowerCase(); + viewtype = Util.null2String(rs4.getString("viewtype")); + if ("0".equals(viewtype)) { + String val = CubeCipherUitl.decrypt(rs.getString(tempFieldName)); + if (StringUtils.isNotBlank(val)) { + if ("".equals(objids)) { + objids = val; + } else { + objids = objids + "," + val; + } + } + } else if ("1".equals(viewtype)) { + if (subbillid != null && !"".equals(subbillid)) { + String psql = "select * from " + tablename + "_dt" + formtype + " where id=" + subbillid; + rs2.executeQuery(psql); + if (rs2.next()) { + String val1 = CubeCipherUitl.decrypt(rs2.getString(tempFieldName)); + if (!"".equals(val1)) { + if ("".equals(objids)) { + objids = val1; + } else { + objids = objids + "," + val1; + } + } + } + } + } + } + if ("1".equals(receiverfieldtype)) { + //人力资源字段 + if (remindhigherlevel == 1) { + //当前人员 + userList = taskService.getRemind_ruleUserList(userList, 1, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (remindhigherlevel == 2) { + // 2 直接上级 + userList = taskService.getRemind_ruleUserList(userList, 7, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (remindhigherlevel == 3) { + // 3 所有上级 + userList = taskService.getRemind_ruleUserList(userList, 8, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (remindhigherlevel == 4) { + // 4 本部门 + // 考虑安全级别 + // 获取 当前字段的部门id + objids = taskService.getDeptOrCompany(objids, "1"); + userList = taskService.getRemind_ruleUserList(userList, 3, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (remindhigherlevel == 5) { + // 5 本分部 + // 考虑安全级别 + // 获取 当前字段的分部id + objids = taskService.getDeptOrCompany(objids, "2"); + userList = taskService.getRemind_ruleUserList(userList, 2, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (remindhigherlevel == 6) { + // 6 本岗位 + // 获取 当前字段的岗位id + objids = taskService.getDeptOrCompany(objids, "3"); + userList = taskService.getRemind_ruleUserList(userList, 6, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } + } else if ("2".equals(receiverfieldtype)) { + //部门 + if (orgrelation == 0) { + userList = taskService.getRemind_ruleUserList(userList, 3, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (orgrelation == 1) { + //所有上级 + userList = taskService.getRemind_ruleUserList(userList, 9, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (orgrelation == 2) { + //所有下级 + userList = taskService.getRemind_ruleUserList(userList, 10, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } + } else if ("3".equals(receiverfieldtype)) { + //分部 + if (orgrelation == 0) { + userList = taskService.getRemind_ruleUserList(userList, 2, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (orgrelation == 1) { + //所有上级 + userList = taskService.getRemind_ruleUserList(userList, 11, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } else if (orgrelation == 2) { + //所有下级 + userList = taskService.getRemind_ruleUserList(userList, 12, objids, Util.getIntValue(receiverlevel), Util.getIntValue(receiverlevel2, -1)); + } + } + } + } + userSet.addAll(userList); + } + return userSet; + } + + private boolean isPush(String billId, String taskDetailId) { + RecordSet recordSet = new RecordSet(); + recordSet.executeQuery("select dygz from uf_jcl_jltsjl where ypz = ? ", billId); + if (recordSet.next()) { + String dygz = recordSet.getString("dygz"); + String[] split = dygz.split(","); + List taskIdList = Arrays.asList(split); + if (taskIdList.contains(taskDetailId)) { + return false; + } else { + // 更新简历推送记录表 + Set list = new HashSet<>(taskIdList); + list.add(taskDetailId); + String taskIds = StringUtils.join(list, ","); + recordSet.executeUpdate("update uf_jcl_jltsjl set dygz = ? where ypz = ? ", taskIds, billId); + } + + } else { + // 插入简历推送记录表 + recordSet.executeUpdate("insert into uf_jcl_jltsjl (ypz,dygz) values (?,?)", billId, taskDetailId); + } + + return true; + } +}