|
@@ -2,14 +2,18 @@ package services
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
+ "github.com/beego/beego/v2/task"
|
|
|
+ "hongze/hongze_task/models"
|
|
|
"hongze/hongze_task/services/company_contract"
|
|
|
"hongze/hongze_task/services/data"
|
|
|
"hongze/hongze_task/utils"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
"sync"
|
|
|
"time"
|
|
|
|
|
|
- "github.com/beego/beego/v2/task"
|
|
|
)
|
|
|
|
|
|
func Task() {
|
|
@@ -57,6 +61,12 @@ func Task() {
|
|
|
//LzExportExcel()
|
|
|
//GetLzProductList()GetLzProductDetail
|
|
|
|
|
|
+ // 定时新增手工指标数据提醒
|
|
|
+ addEdbTask := task.NewTask("sendWaitReport", "1 0 0 * * * ", AddEdbTask)
|
|
|
+ task.AddTask("定时新增手工指标数据提醒", addEdbTask)
|
|
|
+ //每次服务启动都需要执行一次的
|
|
|
+ _ = AddEdbTask(nil)
|
|
|
+
|
|
|
fmt.Println("task end")
|
|
|
}
|
|
|
|
|
@@ -88,8 +98,8 @@ func releaseTask() {
|
|
|
sendEmail := task.NewTask("sendEmail", "0 0 12 * * 0 ", SendEmail)
|
|
|
task.AddTask("sendEmail", sendEmail)
|
|
|
|
|
|
- oneMinute := task.NewTask("oneMinute", "0 */1 7-23 * * * ", OneMinute)
|
|
|
- task.AddTask("oneMinute", oneMinute)
|
|
|
+ //oneMinute := task.NewTask("oneMinute", "0 */1 7-23 * * * ", OneMinute)
|
|
|
+ //task.AddTask("oneMinute", oneMinute)
|
|
|
|
|
|
// 正式/试用 用户到期提醒
|
|
|
companyRemind := task.NewTask("companyRemind", "0 30 08 * * *", CompanyRemind)
|
|
@@ -245,3 +255,432 @@ endData:=time.Now().UnixNano()/1e6
|
|
|
dateTime:=time.Unix(endData/1000,0)
|
|
|
fmt.Println(dateTime)
|
|
|
*/
|
|
|
+
|
|
|
+// EdbTaskNameMap 手工指标定时任务名称map集合
|
|
|
+var EdbTaskNameMap map[string]map[string]bool
|
|
|
+
|
|
|
+// EdbTaskNameChannel 手工指标定时任务名称channel
|
|
|
+var EdbTaskNameChannel chan string
|
|
|
+
|
|
|
+// EdbTaskStopChannel 手工指标定时任务停止channel
|
|
|
+var EdbTaskStopChannel chan string
|
|
|
+
|
|
|
+// EdbTaskRunNum 手工指标定时任务开始次数
|
|
|
+var EdbTaskRunNum int
|
|
|
+
|
|
|
+// AddEdbTask 新增手工指标数据录入提醒
|
|
|
+func AddEdbTask(cont context.Context) (err error) {
|
|
|
+ //func AddEdbTask() (err error) {
|
|
|
+ list, err := models.GetEdbInfoByFrequencyNotDay()
|
|
|
+ if err != nil {
|
|
|
+ fmt.Println("查询获取频度非日度 且 提醒时间不为空 的指标数据失败,Err:", err.Error())
|
|
|
+ }
|
|
|
+ //如果还没有初始化map,那么先初始
|
|
|
+ if EdbTaskNameMap == nil {
|
|
|
+ EdbTaskNameMap = make(map[string]map[string]bool)
|
|
|
+ }
|
|
|
+ tmpEdbTaskNameMap := make(map[string]bool)
|
|
|
+
|
|
|
+ // 今天的日期字符串(格式:2021-10-25)
|
|
|
+ todayStr := time.Now().Format(utils.FormatDate)
|
|
|
+
|
|
|
+ //当前周的周一与周日
|
|
|
+ nowWeekFirstDay := utils.GetNowWeekMonday()
|
|
|
+ nowWeekLastDay := utils.GetNowWeekLastDay()
|
|
|
+
|
|
|
+ //当前月的一号与最后一天
|
|
|
+ nowMonthFirstDay := utils.GetNowMonthFirstDay()
|
|
|
+ nowMonthLastDay := utils.GetNowMonthLastDay()
|
|
|
+
|
|
|
+ //当前季度的第一天与最后一天
|
|
|
+ nowQuarterFirstDay := utils.GetNowQuarterFirstDay()
|
|
|
+ nowQuarterLastDay := utils.GetNowQuarterLastDay()
|
|
|
+
|
|
|
+ //当前半年的第一天与最后一天
|
|
|
+ nowHalfYearFirstDay := utils.GetNowHalfYearFirstDay()
|
|
|
+ nowHalfYearLastDay := utils.GetNowHalfYearLastDay()
|
|
|
+
|
|
|
+ // 当前年的第一天与最后一天
|
|
|
+ nowYearFirstDay := utils.GetNowYearFirstDay()
|
|
|
+ nowYearLastDay := utils.GetNowYearLastDay()
|
|
|
+
|
|
|
+ //失败列表
|
|
|
+ failList := make([]string, 0)
|
|
|
+
|
|
|
+ debugNoticeUserId := 0 //测试环境,需要发送消息的用户
|
|
|
+ if utils.RunMode == "debug" {
|
|
|
+ tmpWxUser, tmpErr := models.GetWxUserByMobile("17634786714")
|
|
|
+ if tmpErr == nil && tmpWxUser != nil {
|
|
|
+ //debugNoticeUserId = 44078 //测试环境的话,发送邮箱给颜鹏
|
|
|
+ debugNoticeUserId = int(tmpWxUser.UserId) //测试环境的话,发送邮箱给嘉豪
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //task.globalTaskManager.adminTaskList
|
|
|
+ for _, edb := range list {
|
|
|
+ tmpEdb := edb //指标信息
|
|
|
+ isNotice := false //是否需要提醒
|
|
|
+ noticeTime := "12:00:00" //提醒时间
|
|
|
+
|
|
|
+ var dataDtTime time.Time
|
|
|
+ edbData, tmpErr := models.GetLastEdbdataInfo(edb.TradeCode)
|
|
|
+ if tmpErr != nil {
|
|
|
+ if tmpErr.Error() != utils.ErrNoRow() {
|
|
|
+ failList = append(failList, fmt.Sprint(edb.TradeCode, "失败,Err:", tmpErr.Error()))
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果确实是有数据的
|
|
|
+ if edbData != nil {
|
|
|
+ tmpDataDtTime, _ := time.ParseInLocation(utils.FormatDate, edbData.Dt, time.Now().Location())
|
|
|
+ dataDtTime = tmpDataDtTime
|
|
|
+ }
|
|
|
+
|
|
|
+ switch edb.Frequency {
|
|
|
+ case "周度":
|
|
|
+ modifyDate := nowWeekLastDay //下次更新日期
|
|
|
+ if edb.NoticeTime != "" {
|
|
|
+ addDay := 7
|
|
|
+ noticeArr := strings.Split(edb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ noticeWeek := noticeArr[0]
|
|
|
+ switch noticeWeek {
|
|
|
+ case "周一":
|
|
|
+ addDay = 1
|
|
|
+ case "周二":
|
|
|
+ addDay = 2
|
|
|
+ case "周三":
|
|
|
+ addDay = 3
|
|
|
+ case "周四":
|
|
|
+ addDay = 4
|
|
|
+ case "周五":
|
|
|
+ addDay = 5
|
|
|
+ case "周六":
|
|
|
+ addDay = 6
|
|
|
+ case "周日":
|
|
|
+ addDay = 7
|
|
|
+ }
|
|
|
+ modifyDate = modifyDate.AddDate(0, 0, addDay-7)
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果正好是提醒日,同时本周没有过记录,那么需要提醒
|
|
|
+ if todayStr == modifyDate.Format(utils.FormatDate) && !nowWeekFirstDay.Before(dataDtTime) {
|
|
|
+ isNotice = true
|
|
|
+ }
|
|
|
+ case "月度":
|
|
|
+ addDay := 0
|
|
|
+ modifyDate := nowMonthLastDay //下次更新日期
|
|
|
+ if edb.NoticeTime != "" {
|
|
|
+ strArr := strings.Split(edb.NoticeTime, "日")
|
|
|
+ if len(strArr) >= 2 {
|
|
|
+ noticeTime = strArr[1]
|
|
|
+ }
|
|
|
+ tmpAddDay, tmpErr := strconv.Atoi(strArr[0])
|
|
|
+ if tmpErr != nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ addDay = tmpAddDay - 1
|
|
|
+ modifyDate = nowMonthFirstDay.AddDate(0, 0, addDay)
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果正好是提醒日,同时本月没有过记录,那么需要提醒
|
|
|
+ if todayStr == modifyDate.Format(utils.FormatDate) && !nowMonthFirstDay.Before(dataDtTime) {
|
|
|
+ isNotice = true
|
|
|
+ }
|
|
|
+ case "季度":
|
|
|
+ //提醒时间
|
|
|
+ if edb.NoticeTime != "" {
|
|
|
+ noticeArr := strings.Split(edb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //每季度更新数据时间
|
|
|
+ //如果正好是提醒日(每季度最后一天),同时本季度没有过记录,那么需要提醒
|
|
|
+ if todayStr == nowQuarterLastDay.Format(utils.FormatDate) && !nowQuarterFirstDay.Before(dataDtTime) {
|
|
|
+ isNotice = true
|
|
|
+ }
|
|
|
+ case "半年度":
|
|
|
+ //提醒时间
|
|
|
+ if edb.NoticeTime != "" {
|
|
|
+ noticeArr := strings.Split(edb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //每半年度更新数据时间
|
|
|
+ //如果正好是提醒日(每半年度最后一天),同时本半年度没有过记录,那么需要提醒
|
|
|
+ if todayStr == nowHalfYearLastDay.Format(utils.FormatDate) && !nowHalfYearFirstDay.Before(dataDtTime) {
|
|
|
+ isNotice = true
|
|
|
+ }
|
|
|
+ case "年度":
|
|
|
+ //提醒时间
|
|
|
+ if edb.NoticeTime != "" {
|
|
|
+ noticeArr := strings.Split(edb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //每年度更新数据时间
|
|
|
+ //如果正好是提醒日(每年度最后一天),同时半年度没有过记录,那么需要提醒
|
|
|
+ if todayStr == nowYearLastDay.Format(utils.FormatDate) && !nowYearFirstDay.Before(dataDtTime) {
|
|
|
+ isNotice = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if isNotice {
|
|
|
+ taskName := "edb_task_" + todayStr + ":" + fmt.Sprint(edb.TradeCode)
|
|
|
+ fmt.Println(taskName, ";", edb.SecName)
|
|
|
+
|
|
|
+ //定时任务
|
|
|
+ tmpTaskFunc := func(ctx context.Context) (funcErr error) {
|
|
|
+ //方法执行结束后,移除定时任务
|
|
|
+ defer func() {
|
|
|
+ EdbTaskNameChannel <- taskName
|
|
|
+ }()
|
|
|
+ // 匿名方法内判断是否发送提醒,因为可能时间到的时候,发现
|
|
|
+ funcIsNotice := false
|
|
|
+ // 再次获取指标数据详情
|
|
|
+ edbData, tmpErr := models.GetLastEdbdataInfo(tmpEdb.TradeCode)
|
|
|
+ if tmpErr != nil {
|
|
|
+ if tmpErr.Error() != utils.ErrNoRow() {
|
|
|
+ funcErr = tmpErr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if utils.RunMode == "debug" && debugNoticeUserId > 0 {
|
|
|
+ tmpEdb.UserId = debugNoticeUserId //测试环境的话,发送邮箱给嘉豪
|
|
|
+ }
|
|
|
+
|
|
|
+ //数据过期时间
|
|
|
+ var funcDataDtTime time.Time
|
|
|
+ //如果确实是有数据的
|
|
|
+ if edbData != nil {
|
|
|
+ tmpDataDtTime, _ := time.ParseInLocation(utils.FormatDate, edbData.Dt, time.Now().Location())
|
|
|
+ funcDataDtTime = tmpDataDtTime
|
|
|
+ }
|
|
|
+
|
|
|
+ switch tmpEdb.Frequency {
|
|
|
+ case "周度":
|
|
|
+ modifyDate := nowWeekLastDay //下次更新日期
|
|
|
+ if tmpEdb.NoticeTime != "" {
|
|
|
+ addDay := 7
|
|
|
+ noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ noticeWeek := noticeArr[0]
|
|
|
+ switch noticeWeek {
|
|
|
+ case "周一":
|
|
|
+ addDay = 1
|
|
|
+ case "周二":
|
|
|
+ addDay = 2
|
|
|
+ case "周三":
|
|
|
+ addDay = 3
|
|
|
+ case "周四":
|
|
|
+ addDay = 4
|
|
|
+ case "周五":
|
|
|
+ addDay = 5
|
|
|
+ case "周六":
|
|
|
+ addDay = 6
|
|
|
+ case "周日":
|
|
|
+ addDay = 7
|
|
|
+ }
|
|
|
+ modifyDate = modifyDate.AddDate(0, 0, addDay-7)
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果正好是提醒日,同时本周没有过记录,那么需要提醒
|
|
|
+ if todayStr == modifyDate.Format(utils.FormatDate) && !nowWeekFirstDay.Before(funcDataDtTime) {
|
|
|
+ funcIsNotice = true
|
|
|
+ }
|
|
|
+ case "月度":
|
|
|
+ addDay := 0
|
|
|
+ modifyDate := nowMonthLastDay //下次更新日期
|
|
|
+ if tmpEdb.NoticeTime != "" {
|
|
|
+ strArr := strings.Split(tmpEdb.NoticeTime, "日")
|
|
|
+ if len(strArr) >= 2 {
|
|
|
+ noticeTime = strArr[1]
|
|
|
+ }
|
|
|
+ tmpAddDay, tmpErr := strconv.Atoi(strArr[0])
|
|
|
+ if tmpErr != nil {
|
|
|
+ funcErr = tmpErr
|
|
|
+ }
|
|
|
+ addDay = tmpAddDay - 1
|
|
|
+ modifyDate = nowMonthFirstDay.AddDate(0, 0, addDay)
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果正好是提醒日,同时本月没有过记录,那么需要提醒
|
|
|
+ if todayStr == modifyDate.Format(utils.FormatDate) && !nowMonthFirstDay.Before(funcDataDtTime) {
|
|
|
+ funcIsNotice = true
|
|
|
+ }
|
|
|
+ case "季度":
|
|
|
+ //提醒时间
|
|
|
+ if tmpEdb.NoticeTime != "" {
|
|
|
+ noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //每季度更新数据时间
|
|
|
+ //如果正好是提醒日(每季度最后一天),同时本季度没有过记录,那么需要提醒
|
|
|
+ if todayStr == nowQuarterLastDay.Format(utils.FormatDate) && !nowQuarterFirstDay.Before(funcDataDtTime) {
|
|
|
+ funcIsNotice = true
|
|
|
+ }
|
|
|
+ case "半年度":
|
|
|
+ //提醒时间
|
|
|
+ if tmpEdb.NoticeTime != "" {
|
|
|
+ noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //每半年度更新数据时间
|
|
|
+ //如果正好是提醒日(每半年度最后一天),同时本半年度没有过记录,那么需要提醒
|
|
|
+ if todayStr == nowHalfYearLastDay.Format(utils.FormatDate) && !nowHalfYearFirstDay.Before(funcDataDtTime) {
|
|
|
+ funcIsNotice = true
|
|
|
+ }
|
|
|
+ case "年度":
|
|
|
+ //提醒时间
|
|
|
+ if tmpEdb.NoticeTime != "" {
|
|
|
+ noticeArr := strings.Split(tmpEdb.NoticeTime, " ")
|
|
|
+ if len(noticeArr) >= 2 {
|
|
|
+ noticeTime = noticeArr[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //每年度更新数据时间
|
|
|
+ //如果正好是提醒日(每年度最后一天),同时半年度没有过记录,那么需要提醒
|
|
|
+ if todayStr == nowYearLastDay.Format(utils.FormatDate) && !nowYearFirstDay.Before(funcDataDtTime) {
|
|
|
+ funcIsNotice = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Println(tmpEdb.TradeCode, " funcIsNotice:", funcIsNotice)
|
|
|
+ //如果还是要提醒
|
|
|
+ if funcIsNotice {
|
|
|
+ //用户微信openid列表数据
|
|
|
+ openIdList := make([]*models.OpenIdList, 0)
|
|
|
+
|
|
|
+ //获取用户信息
|
|
|
+ isAdmin := true
|
|
|
+ admin, err := models.GetAdminByAdminId(tmpEdb.UserId)
|
|
|
+ if err != nil {
|
|
|
+ if err.Error() == utils.ErrNoRow() {
|
|
|
+ isAdmin = false
|
|
|
+ } else {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if admin == nil {
|
|
|
+ isAdmin = false
|
|
|
+ }
|
|
|
+ if isAdmin {
|
|
|
+ if admin.Mobile == "" {
|
|
|
+
|
|
|
+ } else {
|
|
|
+ wxUser, err := models.GetWxUserByMobile(admin.Mobile)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if wxUser == nil {
|
|
|
+ funcErr = errors.New("用户信息不存在:mobile:" + admin.Mobile)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ tmpOpenidList, err := models.GetUserOpenidListByUserId(int(wxUser.UserId))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ openIdList = tmpOpenidList
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ tmpOpenidList, err := models.GetUserOpenidListByUserId(tmpEdb.UserId)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ openIdList = tmpOpenidList
|
|
|
+ }
|
|
|
+ //发送消息
|
|
|
+ if len(openIdList) <= 0 {
|
|
|
+ funcErr = errors.New("openId 列表为空" + strconv.Itoa(tmpEdb.UserId))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ first := "数据录入提醒"
|
|
|
+ keyword1 := tmpEdb.SecName
|
|
|
+ keyword2 := "每周 " + edb.NoticeTime
|
|
|
+ remark := tmpEdb.SecName + "该更新了"
|
|
|
+
|
|
|
+ err = SendWxMsgWithFrequency(first, keyword1, keyword2, remark, openIdList)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ //发送成功,记录发送日志
|
|
|
+ {
|
|
|
+ sendRecord := new(models.EdbinfoSendMsgRecord)
|
|
|
+ sendRecord.UserId = tmpEdb.UserId
|
|
|
+ sendRecord.TradeCode = tmpEdb.TradeCode
|
|
|
+ sendRecord.CreateTime = time.Now()
|
|
|
+ err = models.AddEdbinfoSendMsgRecord(sendRecord)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ //添加定时任务
|
|
|
+ spec := ``
|
|
|
+ if noticeTime != "" {
|
|
|
+ noticeArr := strings.Split(noticeTime, ":")
|
|
|
+ if len(noticeArr) == 3 {
|
|
|
+ //spec = ` */20 * * * * * `
|
|
|
+ spec = fmt.Sprintf(` %s %s %s * * * `, noticeArr[2], noticeArr[1], noticeArr[0])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //定时任务开始的时间
|
|
|
+ tmpTask := task.NewTask(taskName, spec, tmpTaskFunc)
|
|
|
+
|
|
|
+ task.AddTask(taskName, tmpTask)
|
|
|
+ tmpEdbTaskNameMap[taskName] = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //将当天的手工指标加入到手工指标池去
|
|
|
+ EdbTaskNameMap[todayStr] = tmpEdbTaskNameMap
|
|
|
+ //开启协程,用来清除定时任务
|
|
|
+ go deleteTask()
|
|
|
+
|
|
|
+ //如果当前定时任务执行次数大于0次,那么需要往手工指标定时任务停止channel写入数据,用来关闭昨天没有执行的的定时任务
|
|
|
+ if EdbTaskRunNum > 0 {
|
|
|
+ //清除昨天的数据
|
|
|
+ EdbTaskStopChannel <- time.Now().AddDate(0, 0, -1).Format(utils.FormatDate)
|
|
|
+ }
|
|
|
+ //手工指标定时任务开始次数累加
|
|
|
+ EdbTaskRunNum++
|
|
|
+
|
|
|
+ for _, v := range failList {
|
|
|
+ fmt.Println(v)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ //fmt.Println(task.NewMapSorter())
|
|
|
+}
|
|
|
+
|
|
|
+// deleteTask 清除已通知的任务
|
|
|
+func deleteTask() {
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case taskName := <-EdbTaskNameChannel:
|
|
|
+ task.DeleteTask(taskName)
|
|
|
+ delete(EdbTaskNameMap, taskName)
|
|
|
+
|
|
|
+ case dayStr := <-EdbTaskStopChannel: //收到停止信号,先清除掉那一天的定时任务,
|
|
|
+ for taskName := range EdbTaskNameMap[dayStr] {
|
|
|
+ task.DeleteTask(taskName)
|
|
|
+ delete(EdbTaskNameMap, taskName)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|