增加招投标模块
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// =============================================================
|
||||
// CRM 招投标管理 - 到期提醒(开发计划 4.1)
|
||||
// =============================================================
|
||||
//
|
||||
// 不新增表、不引入定时任务:所有提醒都是「按当前时间实时计算」的,
|
||||
// 避免定时任务没跑导致状态陈旧。前端在招投标首页拉取近 N 天的待办。
|
||||
//
|
||||
// 覆盖四类到期:
|
||||
// 1. 投标:报名截止 / 投标截止 / 开标时间
|
||||
// 2. 招标:报名截止 / 投标截止 / 开标时间
|
||||
// 3. 资质:证书到期
|
||||
//
|
||||
// GET /backend/crm/bidding/reminders?days=7
|
||||
// =============================================================
|
||||
|
||||
// BackendCrmBiddingReminderController 招投标到期提醒
|
||||
type BackendCrmBiddingReminderController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// biddingReminderItem 一条到期提醒
|
||||
type biddingReminderItem struct {
|
||||
BizType string `json:"biz_type"` // bid | tender | qualification
|
||||
BizID uint64 `json:"biz_id"` // 业务记录ID
|
||||
Kind string `json:"kind"` // register_end | bid_end | open_bid | expire
|
||||
KindName string `json:"kind_name"` // 提醒类型名称
|
||||
Title string `json:"title"` // 记录标题
|
||||
DueDate string `json:"due_date"` // 到期时间 yyyy-MM-dd HH:mm:ss
|
||||
RemainDays int `json:"remain_days"` // 剩余天数,负值表示已逾期
|
||||
Level string `json:"level"` // danger(<=1天/已逾期) | warning(<=3天) | info
|
||||
OwnerName string `json:"owner_user_name"`
|
||||
}
|
||||
|
||||
// List GET /backend/crm/bidding/reminders?days=7
|
||||
func (c *BackendCrmBiddingReminderController) List() {
|
||||
claims, err := pipelineClaims(&c.Controller)
|
||||
if err != nil {
|
||||
pipelineErr(&c.Controller, 401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
tid := pipelineTenantID(claims)
|
||||
|
||||
days, _ := c.GetInt("days", 7)
|
||||
if days <= 0 || days > 365 {
|
||||
days = 7
|
||||
}
|
||||
now := time.Now()
|
||||
deadline := now.AddDate(0, 0, days)
|
||||
|
||||
items := make([]biddingReminderItem, 0, 32)
|
||||
|
||||
// ---------- 投标侧 ----------
|
||||
var bids []models.TenantCrmBiddingBid
|
||||
_, _ = models.Orm.QueryTable(new(models.TenantCrmBiddingBid)).
|
||||
Filter("tenant_id", tid).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status__in", 0, 1, 2, 3).
|
||||
All(&bids)
|
||||
for _, b := range bids {
|
||||
if !canAccessCrmRecord(claims, b.OwnerUserID, b.CreateUserID) {
|
||||
continue
|
||||
}
|
||||
items = append(items, pickBidReminders(b, now, deadline)...)
|
||||
}
|
||||
|
||||
// ---------- 招标侧 ----------
|
||||
var tenders []models.TenantCrmBiddingTender
|
||||
_, _ = models.Orm.QueryTable(new(models.TenantCrmBiddingTender)).
|
||||
Filter("tenant_id", tid).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status__in", 0, 1, 2, 3, 4, 5).
|
||||
All(&tenders)
|
||||
for _, t := range tenders {
|
||||
if !canAccessCrmRecord(claims, t.OwnerUserID, t.CreateUserID) {
|
||||
continue
|
||||
}
|
||||
items = append(items, pickTenderReminders(t, now, deadline)...)
|
||||
}
|
||||
|
||||
// ---------- 资质到期 ----------
|
||||
var quals []models.TenantCrmBiddingBidQualification
|
||||
_, _ = models.Orm.QueryTable(new(models.TenantCrmBiddingBidQualification)).
|
||||
Filter("tenant_id", tid).
|
||||
Filter("delete_time__isnull", true).
|
||||
All(&quals)
|
||||
for _, q := range quals {
|
||||
if q.ExpireDate == nil {
|
||||
continue
|
||||
}
|
||||
due := *q.ExpireDate
|
||||
// 资质到期包含「已过期」,便于用户发现失效证书
|
||||
if due.After(deadline) {
|
||||
continue
|
||||
}
|
||||
items = append(items, biddingReminderItem{
|
||||
BizType: "qualification",
|
||||
BizID: q.ID,
|
||||
Kind: "expire",
|
||||
KindName: "资质到期",
|
||||
Title: q.QualificationName,
|
||||
DueDate: due.Format("2006-01-02"),
|
||||
RemainDays: daysBetween(now, due),
|
||||
Level: levelOf(daysBetween(now, due)),
|
||||
OwnerName: q.OwnerUserName,
|
||||
})
|
||||
}
|
||||
|
||||
// 按到期时间升序:最紧急的排在最前
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if items[i].RemainDays != items[j].RemainDays {
|
||||
return items[i].RemainDays < items[j].RemainDays
|
||||
}
|
||||
return items[i].DueDate < items[j].DueDate
|
||||
})
|
||||
|
||||
pipelineOk(&c.Controller, map[string]interface{}{
|
||||
"list": items,
|
||||
"total": len(items),
|
||||
"days": days,
|
||||
})
|
||||
}
|
||||
|
||||
// pickBidReminders 从一条投标记录中挑出落在窗口内的到期项
|
||||
func pickBidReminders(b models.TenantCrmBiddingBid, now, deadline time.Time) []biddingReminderItem {
|
||||
out := make([]biddingReminderItem, 0, 3)
|
||||
add := func(kind, kindName string, due *time.Time) {
|
||||
if due == nil {
|
||||
return
|
||||
}
|
||||
d := *due
|
||||
if d.Before(now) || d.After(deadline) {
|
||||
return
|
||||
}
|
||||
out = append(out, biddingReminderItem{
|
||||
BizType: "bid",
|
||||
BizID: b.ID,
|
||||
Kind: kind,
|
||||
KindName: kindName,
|
||||
Title: b.Title,
|
||||
DueDate: d.Format("2006-01-02 15:04:05"),
|
||||
RemainDays: daysBetween(now, d),
|
||||
Level: levelOf(daysBetween(now, d)),
|
||||
OwnerName: b.OwnerUserName,
|
||||
})
|
||||
}
|
||||
add("register_end", "报名截止", b.RegisterEndDate)
|
||||
add("bid_end", "投标截止", b.BidEndDate)
|
||||
add("open_bid", "开标时间", b.OpenBidDate)
|
||||
return out
|
||||
}
|
||||
|
||||
// pickTenderReminders 从一条招标记录中挑出落在窗口内的到期项
|
||||
func pickTenderReminders(t models.TenantCrmBiddingTender, now, deadline time.Time) []biddingReminderItem {
|
||||
out := make([]biddingReminderItem, 0, 3)
|
||||
add := func(kind, kindName string, due *time.Time) {
|
||||
if due == nil {
|
||||
return
|
||||
}
|
||||
d := *due
|
||||
if d.Before(now) || d.After(deadline) {
|
||||
return
|
||||
}
|
||||
out = append(out, biddingReminderItem{
|
||||
BizType: "tender",
|
||||
BizID: t.ID,
|
||||
Kind: kind,
|
||||
KindName: kindName,
|
||||
Title: t.Title,
|
||||
DueDate: d.Format("2006-01-02 15:04:05"),
|
||||
RemainDays: daysBetween(now, d),
|
||||
Level: levelOf(daysBetween(now, d)),
|
||||
OwnerName: t.OwnerUserName,
|
||||
})
|
||||
}
|
||||
add("register_end", "报名截止", t.RegisterEndDate)
|
||||
add("bid_end", "投标截止", t.BidEndDate)
|
||||
add("open_bid", "开标时间", t.OpenBidDate)
|
||||
return out
|
||||
}
|
||||
|
||||
// daysBetween 计算今天(0点)到目标时间的剩余天数
|
||||
func daysBetween(now, target time.Time) int {
|
||||
n := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
t := time.Date(target.Year(), target.Month(), target.Day(), 0, 0, 0, 0, target.Location())
|
||||
return int(t.Sub(n).Hours() / 24)
|
||||
}
|
||||
|
||||
// levelOf 剩余天数 -> 提醒等级
|
||||
func levelOf(days int) string {
|
||||
if days <= 1 {
|
||||
return "danger"
|
||||
}
|
||||
if days <= 3 {
|
||||
return "warning"
|
||||
}
|
||||
return "info"
|
||||
}
|
||||
Reference in New Issue
Block a user