Browse Source

feature(项目):
1、项目详情及升级功能优化
2、上传钉钉文件过慢问题
3、招投标区域划分,按照经销商模式,仅显示账号权限下的区域

ZZH-wl 2 years ago
parent
commit
b0b3b9a05b

+ 2 - 6
opms_admin/app/handler/dept.go

@@ -2,11 +2,8 @@ package handler
 
 import (
 	"context"
-	"dashoo.cn/opms_libary/myerrors"
-	"errors"
-
 	"dashoo.cn/common_definition/comm_def"
-	"github.com/gogf/gf/frame/g"
+	"dashoo.cn/opms_libary/myerrors"
 	"github.com/gogf/gf/util/gvalid"
 
 	"dashoo.cn/micro/app/model"
@@ -19,8 +16,7 @@ type DeptHandler struct{}
 func (h *DeptHandler) GetList(ctx context.Context, req *model.SysDeptSearchParams, rsp *comm_def.CommonMsg) error {
 	deptService, err := service.NewDeptService(ctx)
 	if err != nil {
-		g.Log().Error(err)
-		return errors.New("系统异常,请重新尝试")
+		return err
 	}
 	list, err := deptService.GetList(req)
 	if err != nil {

+ 3 - 3
opms_admin/app/handler/message.go

@@ -44,7 +44,7 @@ func (h *MessageHandler) GetUserHistory(ctx context.Context, req *model.SysMessa
 }
 
 // GetEntityById 详情
-func (o *MessageHandler) GetEntityById(ctx context.Context, req *comm_def.IdReq, rsp *comm_def.CommonMsg) error {
+func (h *MessageHandler) GetEntityById(ctx context.Context, req *comm_def.IdReq, rsp *comm_def.CommonMsg) error {
 	// 参数校验
 	if req.Id == 0 {
 		return myerrors.TipsError("请求参数不存在。")
@@ -99,8 +99,8 @@ func (h *MessageHandler) DeleteByIds(ctx context.Context, req *comm_def.IdsReq,
 	return err
 }
 
-//sendmail 发送邮件
-func (s *MessageHandler) SendMail(ctx context.Context, req g.MapStrStr, rsp *comm_def.CommonMsg) error {
+// SendMail 发送邮件
+func (h *MessageHandler) SendMail(ctx context.Context, req g.MapStrStr, rsp *comm_def.CommonMsg) error {
 	reqData := new(model.SendMessageReq)
 	if err := gconv.Struct(req, reqData); err != nil {
 		return err

+ 2 - 2
opms_admin/app/service/sys_dept.go

@@ -30,10 +30,10 @@ func NewDeptService(ctx context.Context) (svc *DeptService, err error) {
 func (s *DeptService) GetList(searchParams *model.SysDeptSearchParams) ([]*model.SysDept, error) {
 	deptModel := s.Dao.M
 	if searchParams.DeptName != "" {
-		deptModel = deptModel.WhereLike("dept_name", "%"+searchParams.DeptName+"%")
+		deptModel = deptModel.WhereLike(s.Dao.C.DeptName, "%"+searchParams.DeptName+"%")
 	}
 	if searchParams.Status != "" {
-		deptModel = deptModel.Where("status", searchParams.Status)
+		deptModel = deptModel.Where(s.Dao.C.Status, searchParams.Status)
 	}
 	depts := ([]*model.SysDept)(nil)
 	if err := deptModel.OrderAsc(s.Dao.C.Sort).Scan(&depts); err != nil {

+ 8 - 8
opms_libary/plugin/dingtalk/storage/storage.go

@@ -21,19 +21,19 @@ const (
 	CommitFileUploadInfoUrl  = "/v1.0/storage/spaces/%s/files/commit"                    //提交文件
 )
 
-//Storage OA审批
+// Storage OA审批
 type Storage struct {
 	base.Base
 }
 
-//NewStorage init
+// NewStorage init
 func NewStorage(context *context.Context) *Storage {
 	material := new(Storage)
 	material.Context = context
 	return material
 }
 
-//CreateSpaces 创建空间
+// CreateSpaces 创建空间
 func (w *Storage) CreateSpaces(spaceId, unionId string) (response QueryFileUploadInfoResponse, err error) {
 	optionCapabilities := &AddSpaceRequestOptionCapabilities{
 		CanSearch:           utils.Bool(true),
@@ -55,7 +55,7 @@ func (w *Storage) CreateSpaces(spaceId, unionId string) (response QueryFileUploa
 	return response, err
 }
 
-//AddPermission 添加权限
+// AddPermission 添加权限
 func (w *Storage) AddPermission(corpId, spaceId, unionId, roleId, memType string) (response AddPermissionResponse, err error) {
 	option := &AddPermissionRequestOption{
 		Duration: utils.Int64(3600),
@@ -76,7 +76,7 @@ func (w *Storage) AddPermission(corpId, spaceId, unionId, roleId, memType string
 	return response, err
 }
 
-//QueryFileUploadInfo 获取文件上传信息
+// QueryFileUploadInfo 获取文件上传信息
 func (w *Storage) QueryFileUploadInfo(spaceId, unionId string) (response QueryFileUploadInfoResponse, err error) {
 	option := &GetFileUploadInfoRequestOption{
 		StorageDriver:  utils.String("DINGTALK"),
@@ -101,9 +101,9 @@ func (w *Storage) QueryFileDownloadInfo(spaceId, fileId, unionId string) (respon
 
 }
 
-//UploadFile 使用OSS的header加签方式上传文件息
+// UploadFile 使用OSS的header加签方式上传文件息
 func (w *Storage) uploadFile(url string, headers map[string]string, filePath string) (code int, err error) {
-	count := 100
+	count := 1
 
 	file, err := os.Open(filepath.Clean(filePath))
 	if err != nil {
@@ -132,7 +132,7 @@ func (w *Storage) uploadFile(url string, headers map[string]string, filePath str
 	return resp.StatusCode, err
 }
 
-//CommitFile 提交文件
+// CommitFile 提交文件
 func (w *Storage) CommitFile(uploadKey, filename, spaceId, parentId, unionId string) (response CommitFileResponse, err error) {
 	optionAppProperties0 := &CommitFileRequestOptionAppProperties{
 		Name:       utils.String("System"),

+ 6 - 0
opms_parent/app/dao/proj/internal/proj_business.go

@@ -92,7 +92,9 @@ type projBusinessColumns struct {
 	HistoricalTransactionInfo string // 经销商与客户历史成交信息
 	DealerSalesId             string // 关联经销商销售
 	DealerSalesName           string // 经销商销售人员
+	DealerSalesContact        string // 经销商销售电话/微信
 	Accendant                 string // 维护部门及人员
+	CustomerIntentionFactory  string // 客户倾向厂家
 	ProjConversionTime        string // 项目转化时间
 	ProjConversionReason      string // 项目转化原因
 	Remark                    string // 备注
@@ -175,7 +177,9 @@ var (
 			HistoricalTransactionInfo: "historical_transaction_info",
 			DealerSalesId:             "dealer_sales_id",
 			DealerSalesName:           "dealer_sales_name",
+			DealerSalesContact:        "dealer_sales_contact",
 			Accendant:                 "accendant",
+			CustomerIntentionFactory:  "customer_intention_factory",
 			ProjConversionTime:        "proj_conversion_time",
 			ProjConversionReason:      "proj_conversion_reason",
 			Remark:                    "remark",
@@ -260,7 +264,9 @@ func NewProjBusinessDao(tenant string) ProjBusinessDao {
 			HistoricalTransactionInfo: "historical_transaction_info",
 			DealerSalesId:             "dealer_sales_id",
 			DealerSalesName:           "dealer_sales_name",
+			DealerSalesContact:        "dealer_sales_contact",
 			Accendant:                 "accendant",
+			CustomerIntentionFactory:  "customer_intention_factory",
 			ProjConversionTime:        "proj_conversion_time",
 			ProjConversionReason:      "proj_conversion_reason",
 			Remark:                    "remark",

+ 0 - 6
opms_parent/app/handler/proj/business.go

@@ -104,12 +104,6 @@ func (p *BusinessHandler) Create(ctx context.Context, req *projModel.AddProjBusi
 	if err := gvalid.CheckStruct(ctx, req, nil); err != nil {
 		return err
 	}
-	// 校验产品数据
-	for _, v := range req.Products {
-		if err := gvalid.CheckStruct(ctx, v, nil); err != nil {
-			return err
-		}
-	}
 	businessService, err := projSrv.NewBusinessService(ctx)
 	if err != nil {
 		return err

+ 2 - 0
opms_parent/app/model/proj/internal/proj_business.go

@@ -73,7 +73,9 @@ type ProjBusiness struct {
 	HistoricalTransactionInfo string      `orm:"historical_transaction_info" json:"historicalTransactionInfo"` // 经销商与客户历史成交信息
 	DealerSalesId             int         `orm:"dealer_sales_id"             json:"dealerSalesId"`             // 关联经销商销售
 	DealerSalesName           string      `orm:"dealer_sales_name"           json:"dealerSalesName"`           // 经销商销售人员
+	DealerSalesContact        string      `orm:"dealer_sales_contact"        json:"dealerSalesContact"`        // 经销商销售电话/微信
 	Accendant                 string      `orm:"accendant"                   json:"accendant"`                 // 维护部门及人员
+	CustomerIntentionFactory  string      `orm:"customer_intention_factory"  json:"customerIntentionFactory"`  // 客户倾向厂家
 	ProjConversionTime        *gtime.Time `orm:"proj_conversion_time"        json:"projConversionTime"`        // 项目转化时间
 	ProjConversionReason      string      `orm:"proj_conversion_reason"      json:"projConversionReason"`      // 项目转化原因
 	Remark                    string      `orm:"remark"                      json:"remark"`                    // 备注

+ 106 - 111
opms_parent/app/model/proj/proj_business.go

@@ -30,77 +30,74 @@ type ProjBusinessSearchReq struct {
 
 type ProjBusinessRes struct {
 	ProjBusiness
-	CustProvinceId  int         `orm:"cust_province_id"            json:"custProvinceId"` // 所在省ID
-	CustProvince    string      `orm:"cust_province"               json:"custProvince"`   // 所在省
-	CustCityId      int         `orm:"cust_city_id"                json:"custCityId"`     // 所在市ID
-	CustCity        string      `orm:"cust_city"                   json:"custCity"`       // 所在市
-	ContractAmount  float64     `json:"contractAmount"`                                   // 合同金额
-	ProjClosingTime *gtime.Time `json:"projClosingTime"`                                  // 合同金额
+	ContractAmount  float64     `json:"contractAmount"`  // 合同金额
+	ProjClosingTime *gtime.Time `json:"projClosingTime"` // 合同金额
+	FollowCount     int         `json:"followCount"`     // 跟进次数
+
+	QuotationFileList []*ProjBusinessFile `json:"quotationFileList"  ` // 报价单文件
+	Products          []BusinessProduct   `json:"products"       `     // 产品列表
 }
 
 type AddProjBusinessReq struct {
-	NboName          string `json:"nboName"        v:"required#项目名称不能为空"`                                   // 项目名称
-	CustId           int    `json:"custId"        v:"required|min:1#|关联客户不能为空"`                             // 关联客户
-	CustName         string `json:"custName"        v:"required#关联客户不能为空"`                                  // 客户名称
-	NboSource        string `json:"nboSource"        v:"required#项目来源不能为空"`                                 // 项目来源
-	ContactId        int    `json:"contactId"        v:"required|min:1#|关联联系人不能为空"`                         // 关联联系人
-	ContactName      string `json:"contactName"        v:"required#联系人姓名不能为空"`                              // 联系人姓名
-	ContactPostion   string `json:"contactPostion"`                                                         // 联系人岗位
-	ContactTelephone string `json:"contactTelephone"`                                                       // 联系人电话
-	SaleId           int    `json:"saleId"        v:"required|min:1#|归属销售不能为空"`                             // 归属销售
-	SaleName         string `json:"saleName"        v:"required#销售姓名不能为空"`                                  // 销售姓名
-	SalesModel       string `json:"salesModel"        v:"required|in:10,20,30#销售模式不能为空|销售模式不存在"`            // 销售模式(10直销20经销30代理)
-	DistributorId    int    `json:"distributorId"        v:"required-unless:salesModel,10#经销商/代理商不能为空"`     // 经销商/代理商ID
-	DistributorName  string `json:"distributorName"        v:"required-unless:salesModel,10#经销商/代理商名称不能为空"` // 经销商/代理商名称
-	NboType          string `json:"nboType"`                                                                // 项目级别(A 、B 、C 、成交、储备)
-
-	ProductLine string `json:"productLine"        v:"required#产品线不能为空"` // 产品线
-	IsBig       string `json:"isBig"        v:"required#是否大项目不能为空"`     // 是否大项目
-
-	NboBudget        float64     `json:"nboBudget"`        // 项目预算
-	FilingTime       *gtime.Time `json:"filingTime"`       // 项目备案时间
-	PlanPurchaseTime *gtime.Time `json:"planPurchaseTime"` // 计划采购时间
-	EstTransTime     *gtime.Time `json:"estTransTime"`     // 预计成交时间
-	EstTransPrice    float64     `json:"estTransPrice"`    // 预计成交价格
-	RiskProfile      string      `json:"riskProfile"`      // 风险情况
-	Difficulty       string      `json:"difficulty"`       // 困难点
-	Competitor       string      `json:"competitor"`       // 竞争公司
-	Intervention     string      `json:"intervention"`     // 介入情况
-	Remark           string      `json:"remark"`           // 备注
-
-	BidId   int    `json:"bidId"`   // 客户招标Id
-	BidInfo string `json:"bidInfo"` // 客户招标信息
-
-	// 跟进日程
-	//FollowTime     *gtime.Time `json:"followTime"       v:"required#跟进时间不能为空"`    // 跟进时间
-	//FollowUserId   int         `json:"followUserId"`                              // 关联跟进负责人
-	//FollowUserName string      `json:"followUserName"`                            // 跟进负责人姓名
-	//FollowContent  string      `json:"followContent"       v:"required#跟进内容不能为空"` // 跟进内容
-
-	Products []BusinessProduct `json:"products"      v:"required#产品列表不能为空"` // 产品列表
-	//20230330 添加
-	NextFollowTime          *gtime.Time `json:"nextFollowTime"`          // 下次联系时间
-	FinalFollowTime         *gtime.Time `json:"finalFollowTime"`         // 最新跟进时间
-	TechnicalSupportTime    *gtime.Time `json:"technicalSupportTime"`    // 技术支持时间
-	TechnicalSupportName    string      `json:"technicalSupportName"`    // 技术支持人员
-	TechnicalSupportContent string      `json:"technicalSupportContent"` // 技术支持内容
-	ParentReceiver          string      `json:"parentReceiver"`          // 总部对接人
-	CapitalSource           string      `json:"capitalSource"`           // 资金来源
-	NboBudgetTime           *gtime.Time `json:"nboBudgetTime"`           // 项目预算期限
-	ProductSatisfaction     string      `json:"productSatisfaction"`     // 产品/方案满足情况
-	PurchasingWay           string      `json:"purchasingWay"`           // 采购方式
-	PurchasingTime          *gtime.Time `json:"purchasingTime"`          // 采购时间
-	MakerName               string      `json:"makerName"`               // 决策人员
-	MakerDept               string      `json:"makerDept"`               // 决策部门
-	DealerSalesName         string      `json:"dealerSalesName"`         // 经销商销售人员
-	Accendant               string      `json:"accendant"`               // 维护部门及人员
-	IsAdoptDashoo           string      `json:"isAdoptDashoo"`           // 是否采纳大数技术参数
-
+	NboName         string `json:"nboName"            v:"required#项目名称不能为空"`                           // 项目名称
+	CustId          int    `json:"custId"             v:"required|min:1#|关联客户不能为空"`                    // 关联客户
+	CustName        string `json:"custName"           v:"required#关联客户不能为空"`                           // 客户名称
+	ProductLine     string `json:"productLine"        v:"required#产品线不能为空"`                            // 产品线
+	NboSource       string `json:"nboSource"          v:"required#项目来源不能为空"`                           // 项目来源
+	BidId           int    `json:"bidId"              v:"required-if:nboSource,80#客户招标不能为空"`           // 客户招标Id
+	BidInfo         string `json:"bidInfo"            v:"required-if:nboSource,80#客户招标信息不能为空"`         // 客户招标信息
+	SaleId          int    `json:"saleId"             v:"required|min:1#|归属销售不能为空"`                    // 归属销售
+	SaleName        string `json:"saleName"           v:"required#销售姓名不能为空"`                           // 销售姓名
+	SalesModel      string `json:"salesModel"         v:"required|in:10,20,30#销售模式不能为空|销售模式不存在"`       // 销售模式(10直销20经销30代理)
+	DistributorId   int    `json:"distributorId"      v:"required-unless:salesModel,10#经销商/代理商不能为空"`   // 经销商/代理商ID
+	DistributorName string `json:"distributorName"    v:"required-unless:salesModel,10#经销商/代理商名称不能为空"` // 经销商/代理商名称
+	NboType         string `json:"nboType"            v:"required|in:30,50#项目级别不能为空|项目级别错误"`           // 项目级别
+	IsBig           string `json:"isBig"              v:"required#是否大项目不能为空"`                          // 是否大项目
+	Remark          string `json:"remark"             `                                                // 备注
 }
 
 type UpdateProjBusinessReq struct {
-	Id int `p:"id"        v:"required# id不能为空"`
-	*AddProjBusinessReq
+	Id      int    `json:"id"                 v:"required|min:1#|id不能为空"`               // 主键
+	NboType string `json:"nboType"            v:"required|in:10,20,30#项目级别不能为空|项目级别错误"` // 项目级别
+	// 创建
+	NboName         string `json:"nboName"            v:"required#项目名称不能为空"`                                // 项目名称
+	CustId          int    `json:"custId"             v:"required|min:1#|关联客户不能为空"`                         // 关联客户
+	CustName        string `json:"custName"           v:"required#关联客户不能为空"`                                // 客户名称
+	ProductLine     string `json:"productLine"        v:"required#产品线不能为空"`                                 // 产品线
+	NboSource       string `json:"nboSource"          v:"required#项目来源不能为空"`                                // 项目来源
+	BidId           int    `json:"bidId"              v:"required-if:nboSource,80#客户招标不能为空"`                // 客户招标Id
+	BidInfo         string `json:"bidInfo"            v:"required-if:nboSource,80#客户招标信息不能为空"`              // 客户招标信息
+	SaleId          int    `json:"saleId"             v:"required|min:1#|归属销售不能为空"`                         // 归属销售
+	SaleName        string `json:"saleName"           v:"required#销售姓名不能为空"`                                // 销售姓名
+	SalesModel      string `json:"salesModel"         v:"required|in:10,20,30#销售模式不能为空|销售模式错误"`             // 销售模式(10直销20经销30代理)
+	DistributorId   int    `json:"distributorId"      v:"required-unless:salesModel,10|min:1#|经销商/代理商不能为空"` // 经销商/代理商ID
+	DistributorName string `json:"distributorName"    v:"required-unless:salesModel,10#经销商/代理商名称不能为空"`      // 经销商/代理商名称
+	IsBig           string `json:"isBig"              v:"required#是否大项目不能为空"`                               // 是否大项目
+
+	// C => B
+	NboBudget          float64 `json:"nboBudget"               v:"required|min:1#|项目预算不能为空"`                  // 项目预算
+	DealerSalesId      int     `json:"dealerSalesId"           v:"required-with:distributorName#|经销商销售不能为空"`  // 关联经销商销售
+	DealerSalesName    string  `json:"dealerSalesName"         v:"required-with:distributorName#经销商销售名称不能为空"` // 经销商销售名称
+	DealerSalesContact string  `json:"dealerSalesContact"      v:"required-with:distributorName#经销商销售电话不能为空"` // 经销商销售电话/微信
+	EstTransPrice      float64 `json:"estTransPrice"           v:"required#预计出货金额不能为空"`                       // 预计出货金额
+	//QuotationFile     string            `json:"quotationFile"           v:"required#报价单文件不能为空"`   // 报价单文件
+	Products []BusinessProduct `json:"products"                v:"required#产品列表不能为空"` // 产品列表
+	// B => A
+	PurchasingWay    string      `json:"purchasingWay"               v:"required#采购方式不能为空"`       // 采购方式
+	CapitalSource    string      `json:"capitalSource"               v:"required#资金来源不能为空"`       // 资金来源
+	PlanPurchaseTime *gtime.Time `json:"planPurchaseTime"            v:"required#计划采购时间不能为空"`     // 计划采购时间
+	ContactId        int         `json:"contactId"                   v:"required|min:1#|联系人不能为空"` // 关联联系人
+	ContactName      string      `json:"contactName"                 v:"required#联系人姓名不能为空"`      // 联系人姓名
+	ContactTelephone string      `json:"contactTelephone"            v:"required#联系人电话不能为空"`      // 联系人电话
+	//ContactWechat    string      `json:"contactWechat"               v:"required#联系人微信不能为空"`        // 联系人微信
+	//MakerId          int         `json:"makerId"                     v:"required|min:1#|关联决策人不能为空"` // 关联决策人
+	MakerName string `json:"makerName"                   v:"required#决策人姓名不能为空"` // 决策人姓名
+	MakerDept string `json:"makerDept"                   v:"required#决策人部门不能为空"` // 决策人部门
+	//IsAdoptDashoo            string      `json:"isAdoptDashoo"               v:"required#是否采纳大数技术参数不能为空"`   // 是否采纳大数技术参数(上传附件)
+	//DashooParamFile          string      `json:"dashooParamFile"             v:"required-if:isAdoptDashoo,10#大数参数文件不能为空"`       // 大数参数文件
+	Competitor               string `json:"competitor"                  v:"required#竞争公司不能为空"`   // 竞争公司
+	CustomerIntentionFactory string `json:"customerIntentionFactory"    v:"required#客户倾向厂家不能为空"` // 客户倾向厂家
+	Remark                   string `json:"remark"                      `                        // 备注
 }
 
 // BusinessProduct 项目产品
@@ -121,51 +118,49 @@ type BusinessProduct struct {
 
 // BusinessUpgradeReq 项目升级请求
 type BusinessUpgradeReq struct {
-	Id                      int         `json:"id"                      v:"required|min:1#|id不能为空"`                // 主键
-	NboType                 string      `json:"nboType"                 v:"required|in:10,20,30#项目级别不能为空|项目级别错误"`  // 项目级别
-	NboBudget               float64     `json:"nboBudget"               v:"required-unless:nboType,30#项目预算不能为空"`   // 项目预算
-	DistributorId           int         `json:"distributorId"           `                                          // 经销商/代理商ID
-	DistributorName         string      `json:"distributorName"         `                                          // 经销商/代理商名称
-	TechnicalSupportName    string      `json:"technicalSupportName"    v:"required-unless:nboType,30#技术支持人员不能为空"` // 技术支持人员
-	TechnicalSupportContent string      `json:"technicalSupportContent" v:"required-unless:nboType,30#技术支持内容不能为空"` // 技术支持内容
-	TechnicalSupportTime    *gtime.Time `json:"technicalSupportTime"    v:"required-unless:nboType,30#技术支持时间不能为空"` // 技术支持时间
-	//CustomerSatisfaction      string      `json:"customerSatisfaction"`      // 客户满意度 (10很满意、20满意、30较满意、40一般、50不满意)
-	ParentReceiver            string      `json:"parentReceiver"`                                                  // 总部对接人
-	NboBudgetTime             *gtime.Time `json:"nboBudgetTime"`                                                   // 项目预算期限
-	CapitalSource             string      `json:"capitalSource"`                                                   // 资金来源
-	ProductSatisfaction       string      `json:"productSatisfaction"`                                             // 产品/方案满足情况
-	PurchasingWay             string      `json:"purchasingWay"         v:"required-if:nboType,10#采购方式不能为空"`       // 采购方式
-	PurchasingTime            *gtime.Time `json:"purchasingTime"        v:"required-if:nboType,10#采购时间不能为空"`       // 采购时间
-	IsAdoptDashoo             string      `json:"isAdoptDashoo"         v:"required-if:nboType,10#是否采纳大数技术参数不能为空"` // 是否采纳大数技术参数(上传附件)
-	MakerId                   int         `json:"makerId"`                                                         // 关联决策人
-	MakerName                 string      `json:"makerName"`                                                       // 决策人姓名
-	MakerDept                 string      `json:"makerDept"`                                                       // 决策人部门
-	HistoricalTransactionInfo string      `json:"historicalTransactionInfo"`                                       // 经销商与客户历史成交信息
-	DealerSalesId             int         `json:"dealerSalesId"`                                                   // 关联经销商销售
-	DealerSalesName           string      `json:"dealerSalesName"`                                                 // 经销商销售人员
-	Accendant                 string      `json:"accendant"`                                                       // 维护部门及人员
-	Remark                    string      `json:"remark"`                                                          // 备注
-	ProjConversionReason      string      `json:"projConversionReason"   v:"required-if:nboType,30#转化原因不能为空"`      // 项目转化原因
-
-	QuotationFile   string `json:"quotationFile"`   // 报价单文件
-	DashooParamFile string `json:"dashooParamFile"` // 大数参数文件
+	Id      int    `json:"id"                      v:"required|min:1#|id不能为空"`               // 主键
+	NboType string `json:"nboType"                 v:"required|in:10,20,30#项目级别不能为空|项目级别错误"` // 项目级别
+	// C => B
+	NboBudget          float64           `json:"nboBudget"               v:"required-unless:nboType,30#项目预算不能为空"`                      // 项目预算
+	SalesModel         string            `json:"salesModel"              v:"required-unless:nboType,30|in:10,20,30#销售模式不能为空|销售模式错误"`   // 销售模式(10直销20经销30代理)
+	DealerSalesId      int               `json:"dealerSalesId"           v:"required-unless:nboType,30,salesModel,10#经销商销售不能为空"`       // 关联经销商销售
+	DealerSalesName    string            `json:"dealerSalesName"         v:"required-unless:nboType,30,salesModel,10#经销商销售名称不能为空"`     // 经销商销售名称
+	DealerSalesContact string            `json:"dealerSalesContact"       v:"required-unless:nboType,30,salesModel,10#经销商销售电话/微信不能为空"` // 经销商销售电话/微信
+	EstTransPrice      float64           `json:"estTransPrice"           v:"required-unless:nboType,30#预计出货金额不能为空"`                    // 预计出货金额
+	QuotationFile      string            `json:"quotationFile"           v:"required-unless:nboType,30#报价单文件不能为空"`                     // 报价单文件
+	Products           []BusinessProduct `json:"products"                v:"required-unless:nboType,30#产品列表不能为空"`                      // 产品列表
+	// B => A
+	PurchasingWay            string      `json:"purchasingWay"               v:"required-if:nboType,10#采购方式不能为空"`         // 采购方式
+	CapitalSource            string      `json:"capitalSource"               v:"required-if:nboType,10#资金来源不能为空"`         // 资金来源
+	PlanPurchaseTime         *gtime.Time `json:"planPurchaseTime"            v:"required-if:nboType,10#计划采购时间不能为空"`       // 计划采购时间
+	ContactId                int         `json:"contactId"                   v:"required-if:nboType,10#联系人不能为空"`          // 关联联系人
+	ContactName              string      `json:"contactName"                 v:"required-if:nboType,10#联系人姓名不能为空"`        // 联系人姓名
+	ContactTelephone         string      `json:"contactTelephone"            v:"required-if:nboType,10#联系人电话不能为空"`        // 联系人电话
+	MakerId                  int         `json:"makerId"                     v:"required-if:nboType,10#关联决策人不能为空"`        // 关联决策人
+	MakerName                string      `json:"makerName"                   v:"required-if:nboType,10#决策人姓名不能为空"`        // 决策人姓名
+	MakerDept                string      `json:"makerDept"                   v:"required-if:nboType,10#决策人部门不能为空"`        // 决策人部门
+	IsAdoptDashoo            string      `json:"isAdoptDashoo"               v:"required-if:nboType,10#是否采纳大数技术参数不能为空"`   // 是否采纳大数技术参数(上传附件)
+	DashooParamFile          string      `json:"dashooParamFile"             v:"required-if:isAdoptDashoo,10#大数参数文件不能为空"` // 大数参数文件
+	Competitor               string      `json:"competitor"                  v:"required-if:nboType,10#竞争公司不能为空"`         // 竞争公司
+	CustomerIntentionFactory string      `json:"customerIntentionFactory"    v:"required-if:nboType,10#客户倾向厂家不能为空"`       // 客户倾向厂家
+
+	// 储备 => C
+	ProjConversionReason string `json:"projConversionReason"    v:"required-if:nboType,30#转化原因不能为空"` // 项目转化原因
+	Remark               string `json:"remark"`                                                      // 备注
 }
 
 // BusinessDowngradeReq 项目降级请求
 type BusinessDowngradeReq struct {
-	Id                      int         `json:"id"        v:"required|min:1#|id不能为空"`                       // 主键
-	NboType                 string      `json:"nboType"        v:"required|in:10,20,30,50#项目级别不能为空|项目级别错误"` // 项目级别
-	TechnicalSupportName    string      `json:"technicalSupportName"`                                       // 技术支持人员
-	TechnicalSupportContent string      `json:"technicalSupportContent"`                                    // 技术支持内容
-	TechnicalSupportTime    *gtime.Time `json:"technicalSupportTime"`                                       // 技术支持时间
-	Remark                  string      `json:"remark" v:"required# 降级原因不能为空"`                              // 备注原因
+	Id      int    `json:"id"         v:"required|min:1#|id不能为空"`                  // 主键
+	NboType string `json:"nboType"    v:"required|in:10,20,30,50#项目级别不能为空|项目级别错误"` // 项目级别
+	Remark  string `json:"remark"     v:"required# 降级原因不能为空"`                      // 备注原因
 }
 
 // BusinessTransferReq 项目转移请求
 type BusinessTransferReq struct {
-	Id       int    `json:"id"        v:"required|min:1#|id不能为空"`  // 主键
-	UserId   int    `json:"userId"        v:"required# 负责人不能为空"`   // 负责人
-	UserName string `json:"userName"        v:"required# 负责人不能为空"` // 负责人
+	Id       int    `json:"id"         v:"required|min:1#|id不能为空"` // 主键
+	UserId   int    `json:"userId"     v:"required# 负责人不能为空"`      // 负责人
+	UserName string `json:"userName"   v:"required# 负责人不能为空"`      // 负责人
 	Remark   string `json:"remark"`                                // 备注
 }
 
@@ -177,19 +172,19 @@ type BusinessToReserveReq struct {
 
 // BusinessPrimacyContactReq 设置首要联系人请求
 type BusinessPrimacyContactReq struct {
-	Id               int    `json:"id"        v:"required|min:1#|id不能为空"`      // 主键
-	ContactId        int    `json:"contactId"        v:"required#关联联系人不能为空"`   // 关联联系人
-	ContactName      string `json:"contactName"        v:"required#联系人姓名不能为空"` // 联系人姓名
-	ContactPostion   string `json:"contactPostion"`                            // 联系人岗位
-	ContactTelephone string `json:"contactTelephone"`                          // 联系人电话
-	Remark           string `json:"remark"`                                    // 备注
+	Id               int    `json:"id"               v:"required|min:1#|id不能为空"` // 主键
+	ContactId        int    `json:"contactId"        v:"required#关联联系人不能为空"`     // 关联联系人
+	ContactName      string `json:"contactName"      v:"required#联系人姓名不能为空"`     // 联系人姓名
+	ContactPostion   string `json:"contactPostion"`                              // 联系人岗位
+	ContactTelephone string `json:"contactTelephone"`                            // 联系人电话
+	Remark           string `json:"remark"`                                      // 备注
 }
 
 // UpdateBusinessStatusReq 更新项目状态请求
 type UpdateBusinessStatusReq struct {
-	Id        int    `json:"id"        v:"required|min:1#|id不能为空"`                        // 主键
-	NboStatus string `json:"nboStatus"        v:"required|in:10,20,30# 项目状态不能为空|项目状态不存在"` // 项目状态
-	Remark    string `json:"remark"`                                                      // 备注
+	Id        int    `json:"id"          v:"required|min:1#|id不能为空"`                 // 主键
+	NboStatus string `json:"nboStatus"   v:"required|in:10,20,30# 项目状态不能为空|项目状态不存在"` // 项目状态
+	Remark    string `json:"remark"`                                                 // 备注
 }
 
 // BusinessReq 获取项目关联信息

+ 10 - 0
opms_parent/app/service/base_test.go

@@ -52,3 +52,13 @@ func TestCreateSystem(t *testing.T) {
 	}
 
 }
+
+func TestDownloadTempFile(t *testing.T) {
+	url := "http://49.235.240.226:9390/4,80c2839cec44"
+	got, err := DownloadTempFile(url)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(got)
+
+}

+ 15 - 5
opms_parent/app/service/cust/cust_customer_bid_record.go

@@ -66,17 +66,27 @@ func (s CustCustomerBidRecordService) List(ctx context.Context, req *model.CustC
 	ctx = context.WithValue(ctx, "contextService", s)
 	dao := s.CustomerDao.As("customer").DataScope(ctx, "sales_id").
 		LeftJoin(s.Dao.Table, "bid", "bid.cust_id=customer.id").WhereNot("bid.id", 0)
+	// 默认按照产品线
+	productCode, err := service.ColumnString(s.Dao.DB.Table("base_product_auth").Wheref("user_id = ?", s.userInfo.Id), "product_code")
+	if err != nil {
+		return 0, nil, err
+	}
+	g.Log().Infof("CustCustomerBidRecordService List product_code %v", productCode)
+
 	//	系统管理员、总经理、销售总监、销售助理看全部的
-	if service.StringsContains(s.userInfo.Roles, "GeneralManager") || service.StringsContains(s.userInfo.Roles, "SalesDirector") || service.StringsContains(s.userInfo.Roles, "SaleAssociate") || service.StringsContains(s.userInfo.Roles, "SysAdmin") {
+	if service.StringsContains(s.userInfo.Roles, "GeneralManager") ||
+		service.StringsContains(s.userInfo.Roles, "SalesDirector") ||
+		service.StringsContains(s.userInfo.Roles, "SaleAssociate") ||
+		service.StringsContains(s.userInfo.Roles, "SysAdmin") {
 
+	} else if service.StringsContains(s.userInfo.Roles, "GeneralManager") {
+		dao = dao.Where("bid.product_line in (?)", productCode)
 	} else {
-		// 默认按照产品线
-		productCode, err := service.ColumnString(s.Dao.DB.Table("base_product_auth").Wheref("user_id = ?", s.userInfo.Id), "product_code")
+		custIds, err := s.CustomerDao.DataScope(ctx, "sales_id").Where(s.CustomerDao.C.IsPublic, noPublic).Fields(s.CustomerDao.C.Id).Array()
 		if err != nil {
 			return 0, nil, err
 		}
-		g.Log().Infof("CustCustomerBidRecordService List product_code %v", productCode)
-		dao = dao.Where("bid.product_line in (?)", productCode)
+		dao = dao.Where("bid.product_line in (?)", productCode).Where("bid.cust_id in (?)", custIds)
 	}
 	if req.SearchText != "" {
 		likestr := fmt.Sprintf("%%%s%%", req.SearchText)

+ 181 - 95
opms_parent/app/service/proj/business.go

@@ -2,6 +2,7 @@ package proj
 
 import (
 	"context"
+	platDao "dashoo.cn/micro/app/dao/plat"
 	"os"
 
 	contractDao "dashoo.cn/micro/app/dao/contract"
@@ -32,7 +33,9 @@ import (
 
 type businessService struct {
 	*service.ContextService
-	Dao *projDao.ProjBusinessDao
+	Dao       *projDao.ProjBusinessDao
+	FollowDao *platDao.PlatFollowupDao
+	fileDao   *projDao.ProjBusinessFileDao
 }
 
 func NewBusinessService(ctx context.Context) (svc *businessService, err error) {
@@ -41,6 +44,8 @@ func NewBusinessService(ctx context.Context) (svc *businessService, err error) {
 		return nil, err
 	}
 	svc.Dao = projDao.NewProjBusinessDao(svc.Tenant)
+	svc.FollowDao = platDao.NewPlatFollowupDao(svc.Tenant)
+	svc.fileDao = projDao.NewProjBusinessFileDao(svc.Tenant)
 	return svc, nil
 }
 
@@ -91,8 +96,30 @@ func (p *businessService) GetList(req *model.ProjBusinessSearchReq) (total int,
 	return
 }
 
-func (p *businessService) GetEntityById(id int64) (business *model.ProjBusiness, err error) {
+func (p *businessService) GetEntityById(id int64) (business *model.ProjBusinessRes, err error) {
 	err = p.Dao.Where(projDao.ProjBusiness.C.Id, id).Scan(&business)
+	if err != nil {
+		return nil, err
+	}
+	if business == nil {
+		return nil, myerrors.TipsError("项目不存在")
+	}
+	business.FollowCount, err = p.FollowDao.Where(p.FollowDao.C.TargetType, "20").Where(p.FollowDao.C.TargetId, business.Id).Count()
+	if err != nil {
+		return nil, err
+	}
+	productList, err := p.GetBusinessProduct(id)
+	if err != nil {
+		return nil, err
+	}
+	if err := gconv.Structs(productList, &business.Products); err != nil {
+		return nil, err
+	}
+	files, err := p.fileDao.Where(p.fileDao.C.BusId, id).WhereLike(p.fileDao.C.FileSource, "%报价单%").OrderDesc(p.fileDao.C.Id).Limit(1).All()
+	if err != nil {
+		return nil, err
+	}
+	business.QuotationFileList = files
 	return
 }
 
@@ -181,18 +208,6 @@ func (p *businessService) Create(req *model.AddProjBusinessReq) (err error) {
 		return myerrors.TipsError("客户不存在")
 	}
 
-	// 设置默认联系人
-	contact := g.Map{
-		projDao.ProjBusinessContact.C.ContactId: req.ContactId,
-	}
-	service.SetCreatedInfo(contact, p.GetCxtUserId(), p.GetCxtUserName())
-
-	// 设置产品信息
-	totalPrice, products, err := p.setProductInfo(0, req.Products)
-	if err != nil {
-		return err
-	}
-
 	// 获取项目编号
 	nboCode, err := p.getNboCode(customer.CustCode)
 	if err != nil {
@@ -204,12 +219,10 @@ func (p *businessService) Create(req *model.AddProjBusinessReq) (err error) {
 		return
 	}
 	business.NboCode = nboCode
-	//business.NboStatus = StatusOK
 	if business.NboType == "" {
 		business.NboType = StatusC
 	}
 	business.ApproStatus = ApprovalWaiting
-	business.EstTransPrice = totalPrice
 	business.CustProvinceId = customer.CustProvinceId
 	business.CustProvince = customer.CustProvince
 	business.CustCityId = customer.CustCityId
@@ -231,22 +244,6 @@ func (p *businessService) Create(req *model.AddProjBusinessReq) (err error) {
 		if err != nil {
 			return err
 		}
-		// 创建了联系人
-		contact[projDao.ProjBusinessContact.C.BusId] = lastId
-		_, err = projDao.NewProjBusinessContactDao(p.Tenant).TX(tx).Insert(contact)
-		if err != nil {
-			return err
-		}
-
-		// 处理项目产品信息
-		for _, v := range products {
-			v.BusId = int(lastId)
-		}
-		// 添加项目产品
-		_, err = projDao.NewProjBusinessProductDao(p.Tenant).TX(tx).Insert(products)
-		if err != nil {
-			return err
-		}
 		// 添加项目动态
 		dynamics := model.ProjBusinessDynamics{
 			BusId:   int(lastId),
@@ -281,16 +278,16 @@ func (p *businessService) Create(req *model.AddProjBusinessReq) (err error) {
 					Name:  utils.String("客户名称"),
 					Value: utils.String(business.CustName),
 				},
-				{
-					Id:    utils.String("TextField_VZ64Y44LXFK0"),
-					Name:  utils.String("主要联系人"),
-					Value: utils.String(business.ContactName),
-				},
 				{
 					Id:    utils.String("DDSelectField_6CQD451D3800"),
 					Name:  utils.String("项目来源"),
 					Value: utils.String(nboSource),
 				},
+				{
+					Id:    utils.String("TextField_1UQFU5BUEWDC"),
+					Name:  utils.String("历史招标信息"),
+					Value: utils.String(req.BidInfo),
+				},
 				{
 					Id:    utils.String("TextField_AEUWH63LJ0O0"),
 					Name:  utils.String("销售工程师"),
@@ -413,7 +410,7 @@ func (p *businessService) UpdateById(req *model.UpdateProjBusinessReq) error {
 
 	err = p.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
 		// 更新项目
-		updateFieldEx := append(service.UpdateFieldEx, p.Dao.C.SaleId, p.Dao.C.SaleName)
+		updateFieldEx := append(service.UpdateFieldEx, p.Dao.C.SaleId, p.Dao.C.SaleName, p.Dao.C.NboType, p.Dao.C.NboCode)
 		_, err = p.Dao.TX(tx).FieldsEx(updateFieldEx...).WherePri(projDao.ProjBusiness.C.Id, req.Id).Update(businessData)
 		if err != nil {
 			return err
@@ -653,7 +650,7 @@ func (p *businessService) BusinessUpgrade(req *model.BusinessUpgradeReq, fileMap
 	if fileMap == nil {
 		fileMap = make(map[string]*multipart.FileHeader)
 	}
-	if req.NboType == StatusA || req.NboType == StatusB {
+	if (req.NboType == StatusA || req.NboType == StatusB) && !strings.HasPrefix(req.QuotationFile, "dingtalk") {
 		fileMap["quotationFile"], err = service.DownloadTempFile(req.QuotationFile)
 		if err != nil {
 			return err
@@ -687,7 +684,10 @@ func (p *businessService) BusinessUpgrade(req *model.BusinessUpgradeReq, fileMap
 		err = p.BusUpgradeDingEvent(business, req, fileMap)
 		return err
 	})
-	return err
+	if err != nil {
+		return err
+	}
+	return nil
 }
 
 // 获取项目的钉钉审批的升级类型
@@ -713,16 +713,51 @@ func (p *businessService) getBusDingUpgradeType(dbNboType, reqNboType string) st
 
 // BusUpgradeDingEvent 项目升级钉钉审批流调用
 func (p *businessService) BusUpgradeDingEvent(business *model.ProjBusiness, req *model.BusinessUpgradeReq, fileMap map[string]*multipart.FileHeader) error {
+	var err error
 	upgradeType := p.getBusDingUpgradeType(business.NboType, req.NboType)
 	if upgradeType == "" {
 		return myerrors.TipsError("错误的升级类型")
 	}
-	productLine, _ := service.GetDictLabelByTypeAndValue(p.Ctx, "sys_product_line", business.ProductLine)
+	productLineMap, err := service.GetDictDataTreeByType(p.Ctx, "sys_product_line")
+	if err != nil {
+		return err
+	}
+	productLine := gconv.String(productLineMap.Get(business.ProductLine))
+
+	dingProductList := make([][]*workflow.StartProcessInstanceRequestFormComponentValues, 0)
+	for _, item := range req.Products {
+		pInfo := make([]*workflow.StartProcessInstanceRequestFormComponentValues, 0)
+		pInfo = append(pInfo, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("TextField_8P2W917Q5I40"),
+			Name:  utils.String("产品编码"),
+			Value: utils.String(item.ProdCode),
+		})
+		pInfo = append(pInfo, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("TextField_1W2HGNQFLLA80"),
+			Name:  utils.String("产品名称"),
+			Value: utils.String(item.ProdName),
+		})
+		pInfo = append(pInfo, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("TextField_1OPT3MZL76GW0"),
+			Name:  utils.String("产品类别"),
+			Value: utils.String(gconv.String(productLineMap.Get(item.ProdClass))),
+		})
+		pInfo = append(pInfo, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("TextField_O405QBZMPW00"),
+			Name:  utils.String("产品单价"),
+			Value: utils.String(gconv.String(item.ProdPrice)),
+		})
+		pInfo = append(pInfo, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("TextField_23P54AZUW4SG0"),
+			Name:  utils.String("数量"),
+			Value: utils.String(gconv.String(item.ProdNum)),
+		})
+		dingProductList = append(dingProductList, pInfo)
+	}
 
 	// 审批流
 	workflowSrv, _ := workflowService.NewFlowService(p.Ctx)
 	// OMS项目升级 审批
-	var err error
 	var dingReq *workflow.StartProcessInstanceRequest
 	bizCode := business.NboCode + ":" + strconv.Itoa(business.Id)
 	switch req.NboType {
@@ -819,33 +854,28 @@ func (p *businessService) BusUpgradeDingEvent(business *model.ProjBusiness, req
 				},
 				{
 					Id:    utils.String("TextField_1PWK6WHMGITC0"),
-					Name:  utils.String("经销商/代理商"),
-					Value: utils.String(req.DistributorName),
+					Name:  utils.String("渠道销售人员"),
+					Value: utils.String(req.DealerSalesName),
 				},
 				{
-					Id:    utils.String("TextField_X4D3QGARU7K0"),
-					Name:  utils.String("支持内容"),
-					Value: utils.String(req.TechnicalSupportContent),
+					Id:    utils.String("TextField_M06MZK20POG0"),
+					Name:  utils.String("渠道销售电话/微信"),
+					Value: utils.String(req.DealerSalesContact),
 				},
 				{
-					Id:    utils.String("TextField_AEUWH63LJ0O0"),
-					Name:  utils.String("销售工程师"),
-					Value: utils.String(business.SaleName),
-				},
-				{
-					Id:    utils.String("DDDateField_1FW1QZQYBZVK0"),
-					Name:  utils.String("采购时间"),
-					Value: utils.String(gconv.String(req.PurchasingTime.Format("Y-m-d"))),
+					Id:    utils.String("TextField_GL59OGH2Z1S0"),
+					Name:  utils.String("预计出货金额"),
+					Value: utils.String(gconv.String(business.EstTransPrice)),
 				},
 				{
-					Id:    utils.String("DDSelectField_21ASEWDIB3MO0"),
-					Name:  utils.String("采购方式"),
-					Value: utils.String(gconv.String(purchasingWayType[req.PurchasingWay])),
+					Id:    utils.String("TableField_1CGSRAWT8YG0"),
+					Name:  utils.String("产品列表"),
+					Value: utils.String(gconv.String(dingProductList)),
 				},
 				{
-					Id:    utils.String("DDSelectField_5R11VVM6GI00"),
-					Name:  utils.String("是否我司参数"),
-					Value: utils.String(gconv.String(yesOrNoType[req.IsAdoptDashoo])),
+					Id:    utils.String("TextField_AEUWH63LJ0O0"),
+					Name:  utils.String("销售工程师"),
+					Value: utils.String(business.SaleName),
 				},
 				{
 					Id:    utils.String("DDAttachment_KZPWZJS9GHO0"),
@@ -879,6 +909,16 @@ func (p *businessService) BusUpgradeDingEvent(business *model.ProjBusiness, req
 				}
 			}
 		}
+		if strings.HasPrefix(req.QuotationFile, "dingtalk") {
+			arr := strings.Split(req.QuotationFile, ":")
+			quotationFile = append(quotationFile, contractModel.DingFileInfo{
+				SpaceId:  arr[1],
+				FileId:   arr[2],
+				FileName: fmt.Sprintf("%v报价单", business.NboName),
+				FileSize: 10,
+				FileType: "",
+			})
+		}
 		if len(quotationFile) == 0 {
 			return myerrors.TipsError("请上传报价单文件")
 		}
@@ -912,34 +952,39 @@ func (p *businessService) BusUpgradeDingEvent(business *model.ProjBusiness, req
 					Value: utils.String(business.CustName),
 				},
 				{
-					Id:    utils.String("NumberField_1F88MCD0W8KG0"),
-					Name:  utils.String("项目预算"),
-					Value: utils.String(gconv.String(req.NboBudget)),
+					Id:    utils.String("DDSelectField_21ASEWDIB3MO0"),
+					Name:  utils.String("采购方式"),
+					Value: utils.String(gconv.String(purchasingWayType[req.PurchasingWay])),
 				},
 				{
-					Id:    utils.String("TextField_1PWK6WHMGITC0"),
-					Name:  utils.String("经销商/代理商"),
-					Value: utils.String(req.DistributorName),
+					Id:    utils.String("TextField_3AIYCSEQLQC0"),
+					Name:  utils.String("资金来源"),
+					Value: utils.String(req.CapitalSource),
 				},
 				{
-					Id:    utils.String("TextField_X4D3QGARU7K0"),
-					Name:  utils.String("支持内容"),
-					Value: utils.String(req.TechnicalSupportContent),
+					Id:    utils.String("DDDateField_1FW1QZQYBZVK0"),
+					Name:  utils.String("计划采购时间"),
+					Value: utils.String(gconv.String(req.PlanPurchaseTime.Format("Y-m-d"))),
 				},
 				{
-					Id:    utils.String("TextField_AEUWH63LJ0O0"),
-					Name:  utils.String("销售工程师"),
-					Value: utils.String(business.SaleName),
+					Id:    utils.String("TextField_1PWK6WHMGITC0"),
+					Name:  utils.String("客户决策人"),
+					Value: utils.String(req.MakerName),
 				},
 				{
-					Id:    utils.String("DDDateField_1FW1QZQYBZVK0"),
-					Name:  utils.String("采购时间"),
-					Value: utils.String(gconv.String(req.PurchasingTime.Format("Y-m-d"))),
+					Id:    utils.String("TextField_16RIJRRF8B340"),
+					Name:  utils.String("客户决策部门"),
+					Value: utils.String(req.MakerDept),
 				},
 				{
-					Id:    utils.String("DDSelectField_21ASEWDIB3MO0"),
-					Name:  utils.String("采购方式"),
-					Value: utils.String(gconv.String(purchasingWayType[req.PurchasingWay])),
+					Id:    utils.String("TextField_7EE0LTUVUSK0"),
+					Name:  utils.String("客户联系人"),
+					Value: utils.String(req.ContactName),
+				},
+				{
+					Id:    utils.String("TextField_E8TFCZUBV940"),
+					Name:  utils.String("客户联系人电话/微信"),
+					Value: utils.String(req.ContactTelephone),
 				},
 				{
 					Id:    utils.String("DDSelectField_5R11VVM6GI00"),
@@ -951,6 +996,21 @@ func (p *businessService) BusUpgradeDingEvent(business *model.ProjBusiness, req
 					Name:  utils.String("附件"),
 					Value: utils.String(gconv.String(dashooParamFile)),
 				},
+				{
+					Id:    utils.String("TextField_FJI7GL9E8W80"),
+					Name:  utils.String("竞争公司"),
+					Value: utils.String(req.Competitor),
+				},
+				{
+					Id:    utils.String("TextField_1NC9MEUBDBR40"),
+					Name:  utils.String("客户倾向厂家"),
+					Value: utils.String(req.CustomerIntentionFactory),
+				},
+				{
+					Id:    utils.String("TextField_AEUWH63LJ0O0"),
+					Name:  utils.String("销售工程师"),
+					Value: utils.String(business.SaleName),
+				},
 				{
 					Id:    utils.String("DDAttachment_19Y01ZRBFWXS0"),
 					Name:  utils.String("上传报价单"),
@@ -1032,6 +1092,7 @@ func (p *businessService) BusinessUpgradeNotify(flow *workflowModel.PlatWorkflow
 	}
 
 	var data = g.Map{}
+	var updateData *model.BusinessUpgradeReq
 	var remark string
 	if msg.ProcessType == "terminate" {
 		data[p.Dao.C.ApproStatus] = ApprovalReturn
@@ -1047,26 +1108,51 @@ func (p *businessService) BusinessUpgradeNotify(flow *workflowModel.PlatWorkflow
 		if err != nil {
 			return err
 		}
-		updateData := new(model.BusinessUpgradeReq)
+		updateData = new(model.BusinessUpgradeReq)
 		gconv.Struct(dynamics.OpnContent, updateData)
 		data = gconv.Map(updateData)
 		data[p.Dao.C.ApproStatus] = ApprovalOK
 		remarkMap := gconv.Map(dynamics.OpnContent)
 		remark = gconv.String(g.Map{"nboType": remarkMap["nboType"], "origNboType": remarkMap["origNboType"]})
 	}
+	err = p.Dao.Transaction(p.Ctx, func(ctx context.Context, tx *gdb.TX) error {
+		// 添加产品
+		if updateData != nil && (updateData.NboType == "20" || updateData.NboType == "10") {
+			// 设置产品信息
+			totalPrice, products, err := p.setProductInfo(updateData.Id, updateData.Products)
+			if err != nil {
+				return err
+			}
+			// 删除项目产品
+			_, err = projDao.NewProjBusinessProductDao(p.Tenant).TX(tx).Where(projDao.ProjBusinessProduct.C.BusId, updateData.Id).Delete()
+			if err != nil {
+				return err
+			}
+			// 添加项目产品
+			_, err = projDao.NewProjBusinessProductDao(p.Tenant).TX(tx).Insert(products)
+			if err != nil {
+				return err
+			}
+			data[p.Dao.C.EstTransPrice] = totalPrice
+		}
+		// 项目修改
+		_, err = p.Dao.TX(tx).WherePri(business.Id).FieldsEx(service.UpdateFieldEx...).Data(data).Update()
+		if err != nil {
+			return err
+		}
 
-	// 项目修改
-	_, err = p.Dao.WherePri(business.Id).FieldsEx(service.UpdateFieldEx...).Data(data).Update()
-	if err != nil {
-		return err
-	}
-	// 添加项目动态
-	dynamics := model.ProjBusinessDynamics{
-		BusId:   business.Id,
-		OpnType: OpnUpgradeApproval,
-		Remark:  remark,
-	}
-	_, err = p.CreateProjBusinessDynamics(nil, dynamics, data)
+		// 添加项目动态
+		dynamics := model.ProjBusinessDynamics{
+			BusId:   business.Id,
+			OpnType: OpnUpgradeApproval,
+			Remark:  remark,
+		}
+		_, err = p.CreateProjBusinessDynamics(tx, dynamics, data)
+		if err != nil {
+			return err
+		}
+		return nil
+	})
 	if err != nil {
 		return err
 	}
@@ -1175,9 +1261,9 @@ func (p *businessService) BusinessDowngrade(req *model.BusinessDowngradeReq) err
 					Value: utils.String(productLine),
 				},
 				{
-					Id:    utils.String("TextField_X4D3QGARU7K0"),
-					Name:  utils.String("支持内容"),
-					Value: utils.String(req.TechnicalSupportContent),
+					Id:   utils.String("TextField_X4D3QGARU7K0"),
+					Name: utils.String("支持内容"),
+					//Value: utils.String(req.TechnicalSupportContent),
 				},
 				{
 					Id:    utils.String("TextField_AEUWH63LJ0O0"),