浏览代码

feature(招标): 1、招标闭环功能实现(钉钉审批缺少工作流,暂未完成)
2、招标超期功能实现,超过2个月没有创建项目以及没有申请闭环的招投标进入已超期
3、首页增加数值统计:当月新增招标信息、项目来源为招标信息转化

lk 2 年之前
父节点
当前提交
91399c09e7

+ 6 - 0
opms_parent/app/dao/cust/internal/cust_customer_bid_record.go

@@ -40,6 +40,8 @@ type custCustomerBidRecordColumns struct {
 	Title         string // 招标信息标题
 	InfoType      string // 信息分类
 	Bidder        string // 中标单位
+	BiddingTime   string // 招标时间
+	CloseLoopMsg  string // 闭环信息描述
 	Remark        string // 备注
 	CreatedBy     string // 创建者
 	CreatedName   string // 创建人
@@ -68,6 +70,8 @@ var (
 			Title:         "title",
 			InfoType:      "info_type",
 			Bidder:        "bidder",
+			BiddingTime:   "bidding_time",
+			CloseLoopMsg:  "close_loop_msg",
 			Remark:        "remark",
 			CreatedBy:     "created_by",
 			CreatedName:   "created_name",
@@ -98,6 +102,8 @@ func NewCustCustomerBidRecordDao(tenant string) CustCustomerBidRecordDao {
 			Title:         "title",
 			InfoType:      "info_type",
 			Bidder:        "bidder",
+			BiddingTime:   "bidding_time",
+			CloseLoopMsg:  "close_loop_msg",
 			Remark:        "remark",
 			CreatedBy:     "created_by",
 			CreatedName:   "created_name",

+ 14 - 0
opms_parent/app/handler/cust/cust_customer_bid_record.go

@@ -84,3 +84,17 @@ func (c *CustCustomerBidRecordHandler) Delete(ctx context.Context, req *model.Id
 	}
 	return nil
 }
+
+// CloseLoop 闭环
+func (c *CustCustomerBidRecordHandler) CloseLoop(ctx context.Context, req *model.CloseLoopReq, rsp *comm_def.CommonMsg) error {
+	g.Log().Infof("CustCustomerBidRecordHandler.CloseLoop request %#v ", &req)
+	s, err := service.NewCustCustomerBidRecordService(ctx)
+	if err != nil {
+		return err
+	}
+	err = s.CloseLoop(ctx, req)
+	if err != nil {
+		return err
+	}
+	return nil
+}

+ 19 - 0
opms_parent/app/handler/dingtalk/ding_event.go

@@ -393,6 +393,25 @@ func (h *DingHandler) handleBpmsInstanceChange(msg *message.MixMessage, ctx *din
 			return err.Error()
 		}
 		return "success"
+	case model.BidCloseLoop:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			srv, err := custServer.NewCustCustomerBidRecordService(ctx.SubsMessage.Ctx)
+			if err != nil {
+				glog.Error(err)
+				return err.Error()
+			}
+			err = srv.BidCloseLoopApproval(instance, msg)
+			if err != nil {
+				glog.Error(err)
+				return err.Error()
+			}
+		}
+		err = s.Update(instance, msg)
+		if err != nil {
+			glog.Error(err)
+			return err.Error()
+		}
+		return "success"
 	}
 
 	// 以下的代码需要调用钉钉接口查询数据,并且以下接口需要审批实例的数据

+ 5 - 0
opms_parent/app/model/cust/cust_customer_bid_record.go

@@ -55,3 +55,8 @@ type CustCustomerBidRecordUpdateReq struct {
 	BiddingTime   *gtime.Time `json:"BiddingTime"`   // 招标日期
 	Remark        *string     `json:"remark"`
 }
+
+type CloseLoopReq struct {
+	Id           int    `json:"id" v:"required#请输入Id"`
+	CloseLoopMsg string `json:"closeLoopMsg"`
+}

+ 1 - 0
opms_parent/app/model/cust/internal/cust_customer_bid_record.go

@@ -22,6 +22,7 @@ type CustCustomerBidRecord struct {
 	InfoType      string      `orm:"info_type"      json:"infoType"`      // 信息分类
 	Bidder        string      `orm:"bidder"         json:"bidder"`        // 中标单位
 	BiddingTime   *gtime.Time `orm:"bidding_time"   json:"biddingTime"`   // 招标日期
+	CloseLoopMsg  string      `orm:"close_loop_msg" json:"closeLoopMsg"`  // 闭环信息描述
 	Remark        string      `orm:"remark"         json:"remark"`        // 备注
 	CreatedBy     int         `orm:"created_by"     json:"createdBy"`     // 创建者
 	CreatedName   string      `orm:"created_name"   json:"createdName"`   // 创建人

+ 1 - 0
opms_parent/app/model/workflow/plat_workflow.go

@@ -29,6 +29,7 @@ const (
 	DistToDist         = "54" // 代理商转经销商
 	WorkOrderCreate    = "61" // 支持工单创建
 	DeliverOrderCreate = "62" // 交付工单创建
+	BidCloseLoop       = "63" // 招标信息闭环
 )
 
 // PlatWorkflow is the golang structure for table plat_workflow.

+ 2 - 2
opms_parent/app/service/contract/ctr_contract_cron.go

@@ -44,7 +44,7 @@ type ContractCron struct {
 func (c CollectionCron) Run() {
 	tenant := g.Config().GetString("micro_srv.tenant")
 	if tenant == "" {
-		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		glog.Error("租户码未设置,请前往配置")
 		return
 	}
 	// 当前时间
@@ -128,7 +128,7 @@ func (c CollectionCron) Run() {
 func (c ContractCron) Run() {
 	tenant := g.Config().GetString("micro_srv.tenant")
 	if tenant == "" {
-		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		glog.Error("租户码未设置,请前往配置")
 		return
 	}
 	// 当前时间

+ 42 - 2
opms_parent/app/service/cust/cust_cron.go

@@ -20,6 +20,7 @@ func init() {
 	c := cron.New()
 	spec1 := "0 0 20 * * ?" // 每晚八点
 	spec2 := "0 0 18 * * 0" // 每周日晚6点
+	spec3 := "0 0 20 * * ?" // 每晚八点
 
 	// 新增招标信息每晚8点
 	if err := c.AddJob(spec1, NewBidCron{}); err != nil {
@@ -29,6 +30,10 @@ func init() {
 	if err := c.AddJob(spec2, NotFollowBidCron{}); err != nil {
 		glog.Error(err)
 	}
+	// 超过2个月没有创建项目以及没有申请闭环的招投标进入已超期
+	if err := c.AddJob(spec3, OverdueBidCron{}); err != nil {
+		glog.Error(err)
+	}
 
 	c.Start()
 }
@@ -41,11 +46,15 @@ type NewBidCron struct {
 type NotFollowBidCron struct {
 }
 
+// OverdueBidCron 招标信息超期
+type OverdueBidCron struct {
+}
+
 // Run 新增招标信息提醒
 func (c NewBidCron) Run() {
 	tenant := g.Config().GetString("micro_srv.tenant")
 	if tenant == "" {
-		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		glog.Error("租户码未设置,请前往配置")
 		return
 	}
 	// 当前时间
@@ -129,7 +138,7 @@ func (c NewBidCron) Run() {
 func (c NotFollowBidCron) Run() {
 	tenant := g.Config().GetString("micro_srv.tenant")
 	if tenant == "" {
-		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		glog.Error("租户码未设置,请前往配置")
 		return
 	}
 	// 当前时间
@@ -208,6 +217,37 @@ func (c NotFollowBidCron) Run() {
 	}
 }
 
+func (c OverdueBidCron) Run() {
+	tenant := g.Config().GetString("micro_srv.tenant")
+	if tenant == "" {
+		glog.Error("租户码未设置,请前往配置")
+		return
+	}
+	date := gtime.Now().AddDate(0, -2, 0).Format("Y-m-d 00:00:00")
+	// 从项目发布日期起,超过2个月没有创建项目以及没有申请闭环的招投标进入已超期
+	// 状态:(10跟进中,20待审批,30已闭环、40已超期)
+	bidInfos, err := g.DB(tenant).Model("cust_customer_bid_record a").LeftJoin("proj_business b", "b.bid_id=a.id").Where(fmt.Sprintf("b.id IS NULL AND a.status='10' AND a.created_time<'%v'", date)).Group("a.id").Fields("a.*").All()
+	if err != nil {
+		glog.Error(err)
+		return
+	}
+	if len(bidInfos) > 0 {
+		ids := ""
+		for _, bid := range bidInfos {
+			if ids == "" {
+				ids = gconv.String(bid["id"])
+			} else {
+				ids += "," + gconv.String(bid["id"])
+			}
+		}
+		_, err = g.DB(tenant).Model("cust_customer_bid_record").Update("status='40'", fmt.Sprintf("id IN (%v)", ids))
+		if err != nil {
+			glog.Error(err)
+			return
+		}
+	}
+}
+
 func uploadFile(file string) (string, error) {
 	var request corpconversation.UploadConversationFileRequest
 	request.Type = "file"

+ 98 - 6
opms_parent/app/service/cust/cust_customer_bid_record.go

@@ -2,21 +2,24 @@ package cust
 
 import (
 	"context"
-	"dashoo.cn/micro/app/service"
-	"database/sql"
-	"fmt"
-	"github.com/gogf/gf/frame/g"
-
 	dao "dashoo.cn/micro/app/dao/cust"
 	model "dashoo.cn/micro/app/model/cust"
+	workflowModel "dashoo.cn/micro/app/model/workflow"
+	"dashoo.cn/micro/app/service"
 	"dashoo.cn/opms_libary/micro_srv"
 	"dashoo.cn/opms_libary/myerrors"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
 	"dashoo.cn/opms_libary/request"
-
+	"database/sql"
+	"fmt"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/os/gtime"
 	"github.com/gogf/gf/util/gvalid"
 )
 
+var BidCloseLoopCode = "" // 客户招标信息闭环审批
+
 type CustCustomerBidRecordService struct {
 	Dao         *dao.CustCustomerBidRecordDao
 	CustomerDao *dao.CustCustomerDao
@@ -246,3 +249,92 @@ func (s CustCustomerBidRecordService) Delete(ctx context.Context, id []int) erro
 	_, err := s.Dao.Where("Id  IN (?)", id).Delete()
 	return err
 }
+
+// CloseLoop 闭环
+func (s CustCustomerBidRecordService) CloseLoop(ctx context.Context, req *model.CloseLoopReq) error {
+	// 数据校验
+	validErr := gvalid.CheckStruct(ctx, req, nil)
+	if validErr != nil {
+		return myerrors.TipsError(validErr.Current().Error())
+	}
+
+	bid, err := s.Dao.Where("id=?", req.Id).One()
+	if err != nil {
+		return err
+	}
+	// 校验 状态:(10跟进中,20待审批,30已闭环、40已超期)
+	if bid.Status != "10" && bid.Status != "40" {
+		return myerrors.TipsError("该招标信息不可闭环操作")
+	}
+
+	err = s.Dao.Transaction(context.TODO(), func(ctx1 context.Context, tx *gdb.TX) error {
+		// 改为待审批状态
+		_, err = s.Dao.Update(fmt.Sprintf("status='20',close_loop_msg='%v'", req.CloseLoopMsg), fmt.Sprintf("id='%v'", req.Id))
+		if err != nil {
+			return err
+		}
+		// 调用钉钉审批
+		//// 审批流
+		//workflowSrv, _ := workflowService.NewFlowService(ctx)
+		//bizCode := strconv.Itoa(req.Id)
+		//_, err = workflowSrv.StartProcessInstance(bizCode, workflowModel.BidCloseLoop, "", &workflow.StartProcessInstanceRequest{
+		//	ProcessCode: &BidCloseLoopCode,
+		//	FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{
+		//		{
+		//			Id:    utils.String("TextField-K2AD4O5B"),
+		//			Name:  utils.String("闭环信息"),
+		//			Value: utils.String(req.CloseLoopMsg),
+		//		},
+		//	},
+		//})
+		//if err != nil {
+		//	g.Log().Error(err)
+		//	return err
+		//}
+
+		return err
+	})
+	return err
+}
+
+// BidCloseLoopApproval 闭环审批结束
+func (s CustCustomerBidRecordService) BidCloseLoopApproval(flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error {
+	bid, err := s.Dao.Where("id = ?", flow.BizCode).FindOne()
+	if err != nil {
+		return fmt.Errorf("闭环审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	if err != nil {
+		return err
+	}
+	if bid == nil {
+		return fmt.Errorf("招标信息不存在:%s Id: %d", flow.BizCode, flow.Id)
+	}
+
+	if msg.ProcessType != "finish" && msg.ProcessType != "terminate" {
+		return fmt.Errorf("无法识别的 ProcessType :%s", msg.ProcessType)
+	}
+	if msg.Result != "agree" && msg.Result != "refuse" && msg.Result != "" {
+		return fmt.Errorf("无法识别的 Result :%s", msg.Result)
+	}
+	// status状态:(10跟进中,20待审批,30已闭环、40已超期)
+	if msg.ProcessType == "terminate" {
+		_, err = s.Dao.Update("status='10'", fmt.Sprintf("id='%v'", bid.Id))
+		if err != nil {
+			return err
+		}
+	} else {
+		if msg.Result == "agree" {
+			_, err = s.Dao.Update("status='30'", fmt.Sprintf("id='%v'", bid.Id))
+			if err != nil {
+				return err
+			}
+		} else if msg.Result == "refuse" {
+			_, err = s.Dao.Update("status='10'", fmt.Sprintf("id='%v'", bid.Id))
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return err
+}

+ 29 - 3
opms_parent/app/service/home/home.go

@@ -4,13 +4,14 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"strings"
 	"time"
 
+	baseDao "dashoo.cn/micro/app/dao/base"
 	contDao "dashoo.cn/micro/app/dao/contract"
 	custDao "dashoo.cn/micro/app/dao/cust"
 	platDao "dashoo.cn/micro/app/dao/plat"
 	projDao "dashoo.cn/micro/app/dao/proj"
-	baseDao "dashoo.cn/micro/app/dao/base"
 	"dashoo.cn/micro/app/model/home"
 	"dashoo.cn/micro/app/model/plat"
 	"dashoo.cn/micro/app/service"
@@ -94,7 +95,7 @@ func (s *HomeService) QueryHomeDataReportData(param *home.SearchDataReportData)
 // 20000-30000之间:报表数据 TODO 疑似与 sys_report 表对应?
 func (s *HomeService) getReportData(id int64, params *map[string]interface{}) (interface{}, error) {
 	switch id {
-	case 10000, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10017, 10018:
+	case 10000, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10017, 10018, 10019, 10020:
 		// 获取数值指标统计数据
 		return s.getNumStatisticsData(id, params)
 	case 20000:
@@ -145,6 +146,7 @@ func (s *HomeService) getNumStatisticsData(id int64, params *map[string]interfac
 	contractCollDao := contDao.NewCtrContractCollectionDao(s.Tenant)
 	distDao := baseDao.NewBaseDistributorDao(s.Tenant)
 	taskDao := platDao.NewPlatTaskDao(s.Tenant)
+	bidDao := custDao.NewCustCustomerBidRecordDao(s.Tenant)
 	currentTime := gtime.Now()
 	monthStart := currentTime.StartOfMonth()
 	monthEnd := currentTime.EndOfMonth()
@@ -227,11 +229,35 @@ func (s *HomeService) getNumStatisticsData(id int64, params *map[string]interfac
 			Sum(contractDao.C.ContractAmount + " - " + contractDao.C.CollectedAmount)
 		return gconv.String(count), err
 	case 10017: //当月新增经销商数量:统计自然月当月新创建的经销商数量。
-		count, err := distDao.Where("dist_type = '10'").Where("created_time >= ?",monthStart).Where("created_time <= ?",monthEnd).Count()
+		count, err := distDao.Where("dist_type = '10'").Where("created_time >= ?", monthStart).Where("created_time <= ?", monthEnd).Count()
 		return gconv.String(count), err
 	case 10018: //进行中的督办:统计正在进行的督办数量。
 		count, err := taskDao.Where("task_status = '20'").Count()
 		return gconv.String(count), err
+	case 10019: //当月新增招标信息
+		// 数据权限
+		//	系统管理员、总经理、销售总监、销售助理看全部的
+		where := ""
+		if service.StringsContains(s.CxtUser.Roles, "GeneralManager") || service.StringsContains(s.CxtUser.Roles, "SalesDirector") || service.StringsContains(s.CxtUser.Roles, "SaleAssociate") || service.StringsContains(s.CxtUser.Roles, "SysAdmin") {
+
+		} else {
+			// 默认按照产品线
+			productCode, err := service.ColumnString(bidDao.DB.Model("base_product_auth").Wheref("user_id = ?", s.CxtUser.Id), "product_code")
+			if err != nil {
+				return "", err
+			}
+			g.Log().Infof("CustCustomerBidRecordService List product_code %v", productCode)
+			if len(productCode) > 0 {
+				where = fmt.Sprintf("product_line in (%v)", strings.Join(productCode, ","))
+			} else {
+				where = "1=0"
+			}
+		}
+		count, err := bidDao.Where(fmt.Sprintf("created_time LIKE '%v%%'", currentTime.Format("Y-m"))).Where(where).Count()
+		return gconv.String(count), err
+	case 10020: //项目来源为招标信息转化
+		count, err := businessDao.Where("bid_id<>0").Count()
+		return gconv.String(count), err
 	}
 	return "", nil
 }

+ 1 - 1
opms_parent/app/service/work/deliver_order_cron.go

@@ -31,7 +31,7 @@ type deliverOrderProgressCron struct {
 func (c deliverOrderProgressCron) Run() {
 	tenant := g.Config().GetString("micro_srv.tenant")
 	if tenant == "" {
-		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		glog.Error("租户码未设置,请前往配置")
 		return
 	}
 	// 当前时间

+ 6 - 1
opms_parent/app/service/work/deliver_order_progress.go

@@ -424,6 +424,11 @@ func (s DeliverOrderProgressService) DeliverGoods(ctx context.Context, req *work
 	if err != nil {
 		return err
 	}
+	// 获取硬件交付工单数据
+	order, err := s.OrderDao.Where(fmt.Sprintf("id='%v'", progress.DeliverOrderId)).One()
+	if err != nil {
+		return err
+	}
 	// 10发货任务单/20组装任务单/30部署安装单
 	installProgress, err := s.Dao.Where(fmt.Sprintf("deliver_order_id='%v' AND progress_status<>'30' AND progress_type='30'", progress.DeliverOrderId)).FindOne()
 	if err != nil && err != sql.ErrNoRows {
@@ -461,7 +466,7 @@ func (s DeliverOrderProgressService) DeliverGoods(ctx context.Context, req *work
 			insProgress.PlanId = int(planId)
 			insProgress.DeliverOrderId = progress.DeliverOrderId
 			insProgress.DeliverProgressId = req.Id
-			insProgress.ProgressTitle = "安装任务单"
+			insProgress.ProgressTitle = fmt.Sprintf("[%v][%v]安装任务单", order.ContractCode, order.ProjectName)
 			insProgress.StartDate = gtime.Now()
 			insProgress.EndDate = progress.EndDate
 			insProgress.ProgressStatus = "10" // 状态(10未开始20进行中30已完成

+ 1 - 1
opms_parent/app/service/work/work_order_cron.go

@@ -31,7 +31,7 @@ type WorkOrderCron struct {
 func (c WorkOrderCron) Run() {
 	tenant := g.Config().GetString("micro_srv.tenant")
 	if tenant == "" {
-		glog.Error("交接任务单定时任务租户码未设置,请前往配置")
+		glog.Error("租户码未设置,请前往配置")
 		return
 	}