Browse Source

feat(合同模块):新增合同审批模块

Roc 3 years ago
parent
commit
b6115a1693

+ 7 - 1
controllers/approval.go

@@ -494,7 +494,13 @@ func (this *ApprovalCommon) ApplyApprove() {
 		content := companyProduct.CompanyName + " " + approvalItem.ApproveContent + approveContent
 		approvalSysUser, _ := admin.GetAdminById(approvalItem.ApplyUserId)
 		if sysUser != nil {
-			go services.AddCompanyApprovalMessage(sysUser.AdminId, approvalItem.ApplyUserId, req.CompanyId, approvalItem.CompanyApprovalId, 2, companyProduct.CompanyName, content, content, approvalSysUser.Mobile)
+			approvalStatus := 1
+			if req.Status == 1 {
+				approvalStatus = 2
+			} else {
+				approvalStatus = 3
+			}
+			go services.AddCompanyApprovalMessage(sysUser.AdminId, approvalItem.ApplyUserId, req.CompanyId, approvalItem.CompanyApprovalId, 2, 1, approvalStatus, companyProduct.CompanyName, content, content, approvalSysUser.Mobile)
 		}
 	}
 	this.OkWithMessage("审批操作成功")

+ 2 - 2
controllers/company_contract.go

@@ -9,7 +9,7 @@ import (
 )
 
 //客户合同模块
-type ContractCommon struct {
+type CompanyContractCommon struct {
 	BaseAuth
 }
 
@@ -19,7 +19,7 @@ type ContractCommon struct {
 // @Param   CompanyContractId   query   int  true       "合同id"
 // @Success 200 {object} company_contract.CompanyContractDetail
 // @router /detail [get]
-func (this *ContractCommon) ApplyContractDetail() {
+func (this *CompanyContractCommon) ApplyContractDetail() {
 	sysUser := this.AdminWx
 
 	companyId, _ := this.GetInt("CompanyId")

+ 96 - 0
controllers/contract.go

@@ -0,0 +1,96 @@
+package controllers
+
+import (
+	"hongze/hongze_mobile_admin/models/response/contract"
+	"hongze/hongze_mobile_admin/models/tables/company_contract"
+	"hongze/hongze_mobile_admin/models/tables/company_report_permission"
+	"hongze/hongze_mobile_admin/services"
+	"hongze/hongze_mobile_admin/utils"
+)
+
+//合同模块
+type ContractCommon struct {
+	BaseAuth
+}
+
+// @Title 合同详情详情
+// @Description 合同详情接口接口
+// @Param   CompanyId   query   int  true       "客户ID"
+// @Param   CompanyContractId   query   int  true       "合同id"
+// @Success 200 {object} company_contract.CompanyContractDetail
+// @router /detail [get]
+func (this *ContractCommon) ApplyContractDetail() {
+	sysUser := this.AdminWx
+
+	companyId, _ := this.GetInt("CompanyId")
+	if companyId <= 0 {
+		this.FailWithMessage("参数错误", "参数错误,客户id小于等于0")
+		return
+	}
+	companyContractId, _ := this.GetInt("CompanyContractId")
+
+	roleTypeCode := sysUser.RoleTypeCode
+	productId := services.GetProductId(roleTypeCode)
+	if productId == 0 {
+		this.FailWithMessage("当前账户类型异常", "客户类型为:"+roleTypeCode)
+		return
+	}
+	detail, err := company_contract.GetCompanyContractDetail(companyId, productId, companyContractId)
+	if err != nil {
+		this.FailWithMessage("获取信息失败", "获取信息失败,Err:"+err.Error())
+		return
+	}
+	productId = detail.ProductId
+	if productId == 1 {
+		for _, v := range utils.PermissionFiccClassifyArr {
+			checkList := make([]int, 0)
+			plist := new(company_report_permission.PermissionLookList)
+			items, err := company_report_permission.GetPermissionLookItems(productId, v)
+			if err != nil {
+				this.FailWithMessage("获取失败", "获取权限信息失败,Err:"+err.Error())
+				return
+			}
+			for _, n := range items {
+				count, err := company_contract.GetCompanyContractPermissionCheckByContractId(companyId, detail.CompanyContractId, n.ChartPermissionId)
+				if err != nil {
+					this.FailWithMessage("获取失败", "获取权限信息失败,Err:"+err.Error())
+					return
+				}
+				if count > 0 {
+					checkList = append(checkList, n.ChartPermissionId)
+				}
+			}
+			plist.Items = items
+			plist.ClassifyName = v
+			plist.CheckList = checkList
+			detail.PermissionList = append(detail.PermissionList, plist)
+		}
+	} else {
+		classifyName := "权益"
+		checkList := make([]int, 0)
+		plist := new(company_report_permission.PermissionLookList)
+		items, err := company_report_permission.GetPermissionLookItems(productId, classifyName)
+		if err != nil {
+			this.FailWithMessage("获取失败", "获取权限信息失败,Err:"+err.Error())
+			return
+		}
+		for _, n := range items {
+			count, err := company_contract.GetCompanyContractPermissionCheckByContractId(companyId, detail.CompanyContractId, n.ChartPermissionId)
+			if err != nil {
+				this.FailWithMessage("获取失败", "获取权限信息失败,Err:"+err.Error())
+				return
+			}
+			if count > 0 {
+				checkList = append(checkList, n.ChartPermissionId)
+			}
+		}
+		plist.Items = items
+		plist.ClassifyName = classifyName
+		plist.CheckList = checkList
+		detail.PermissionList = append(detail.PermissionList, plist)
+	}
+	resp := contract.CompanyContractDetailResp{
+		detail,
+	}
+	this.OkDetailed(resp, "获取成功")
+}

+ 274 - 0
controllers/contract_approval.go

@@ -0,0 +1,274 @@
+package controllers
+
+import (
+	"encoding/json"
+	contractReq "hongze/hongze_mobile_admin/models/request/contract"
+	"hongze/hongze_mobile_admin/models/response/contract"
+	contractTable "hongze/hongze_mobile_admin/models/tables/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract_approval"
+	"hongze/hongze_mobile_admin/models/tables/contract_approval_record"
+	contractService "hongze/hongze_mobile_admin/services/contract"
+	"hongze/hongze_mobile_admin/utils"
+	"rdluck_tools/paging"
+	"strconv"
+	"strings"
+)
+
+//合同模块
+type ContractApprovalCommon struct {
+	BaseAuth
+}
+
+// @Title 审批单列表
+// @Description 审批单列表接口
+// @Param   ContractType   query   string  false       "合同类型,枚举值:'新签合同','续约合同','补充协议'"
+// @Param   Status   query   string  false       "合同状态,枚举值:'待审批','已审批','已驳回','已撤回'"
+// @Param   ProductId   query   int  false       "客户类型:传0或者不传为当前账号权限,1 代表是:ficc;2 代表是:权益"
+// @Param   SellerId   query   string  false       "选择的销售id"
+// @Param   Keyword   query   string  false       "搜索关键字"
+// @Param   ModifyStartTime   query   string  false       "服务更新时间的选择开始时间,格式:2021-05-23 00:00:00"
+// @Param   ModifyEndTime   query   string  false       "服务更新时间的选择结束时间,格式:2021-05-26 23:59:59"
+// @Success 200 {object} response.ContractApprovalListResp
+// @router /list [get]
+func (this *ContractApprovalCommon) List() {
+	sysUser := this.AdminWx
+
+	//合同类型、产品类型、合同状态、更新时间、所选销售
+	//关键字:合同编号、客户名称,社会信用码
+	contractType := this.GetString("ContractType")
+	status := this.GetString("Status")
+	productId, _ := this.GetInt("ProductId")
+	sellerIds := this.GetString("SellerId")
+	keyword := this.GetString("Keyword")
+	modifyStartTime := this.GetString("ModifyStartTime")
+	modifyEndTime := this.GetString("ModifyEndTime")
+
+	childCondition := ""
+	condition := ""
+	childPars := make([]interface{}, 0)
+	pars := make([]interface{}, 0)
+	//如果不是超管或者合规,那么只能查看自己的合同
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN { //权益管理员
+		condition += ` AND c.product_id = ? `
+		pars = append(pars, 2)
+	} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN { //ficc管理员
+		condition += ` AND c.product_id = ? `
+		pars = append(pars, 1)
+	} else {
+		if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_COMPLIANCE && sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+			condition += ` AND c.seller_id = ? `
+			pars = append(pars, sysUser.AdminId)
+		}
+	}
+
+	//合同类型、、更新时间、所选销售
+	//关键字:合同编号、客户名称,社会信用码
+	if contractType != "" {
+		condition += ` AND c.contract_type = ? `
+		pars = append(pars, contractType)
+	}
+	//审批状态
+	if status != "" {
+		//childCondition += ` AND status = ? `
+		//childPars = append(childPars, status)
+
+		if status == "已审批" {
+			condition += ` AND c.status in ("已审批","已驳回")  `
+		} else {
+			condition += ` AND c.status = ? `
+			pars = append(pars, status)
+		}
+	} else {
+		//childCondition += ` AND status != "已撤回" `
+
+		condition += ` AND c.status not in ("已撤回","待提交")  `
+	}
+	//产品类型
+	if productId > 0 {
+		condition += ` AND c.product_id = ? `
+		pars = append(pars, productId)
+	}
+	//所选销售
+	if sellerIds != "" {
+		condition += ` AND c.seller_id IN (` + sellerIds + `) `
+	}
+	//更新开始时间
+
+	//更新开始时间
+	if modifyStartTime != "" {
+		condition += ` AND a.modify_time >= ? `
+		pars = append(pars, modifyStartTime)
+	}
+	//更新结束时间
+	if modifyEndTime != "" {
+		condition += ` AND a.modify_time <= ? `
+		pars = append(pars, modifyEndTime)
+	}
+	//关键字
+	if keyword != "" {
+		condition += ` AND (c.contract_code LIKE '%` + keyword + `%' OR c.company_name LIKE '%` + keyword + `%' OR c.credit_code LIKE '%` + keyword + `%' ) `
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	total, err := contract_approval.GetContractApprovalListCount(condition, pars)
+	if err != nil {
+		this.FailWithMessage("获取失败", "获取数据总数失败,Err:"+err.Error())
+		return
+	}
+
+	list, err := contract_approval.GetContractApprovalList(childCondition, condition, childPars, pars, startSize, pageSize)
+	if err != nil {
+		this.FailWithMessage("获取审批列表失败!", "获取审批列表失败,Err:"+err.Error())
+		return
+	}
+
+	if len(list) > 0 {
+		contractApprovalIdSlice := make([]string, 0)
+		for i := 0; i < len(list); i++ {
+			contractApprovalIdSlice = append(contractApprovalIdSlice, strconv.Itoa(list[i].ContractApprovalId))
+		}
+		contractApprovalIdStr := strings.Join(contractApprovalIdSlice, ",")
+
+		//获取审批流列表数据
+		contractApprovalRecordList, err := contract_approval_record.GetContractApprovalRecordList(contractApprovalIdStr, sysUser.AdminId)
+		if err != nil {
+			this.FailWithMessage("获取审批列表失败!", "获取审批列表失败,Err:"+err.Error())
+			return
+		}
+		contractApprovalRecordMap := make(map[int]*contract_approval_record.ContractApprovalRecord)
+		for i := 0; i < len(contractApprovalRecordList); i++ {
+			contractApprovalRecordMap[contractApprovalRecordList[i].ContractApprovalId] = contractApprovalRecordList[i]
+		}
+
+		for i := 0; i < len(list); i++ {
+			item := list[i]
+			list[i].StartDateStr = item.StartDate.Format(utils.FormatDate)
+			list[i].EndDateStr = item.EndDate.Format(utils.FormatDate)
+			list[i].CreateTimeStr = item.CreateTime.Format(utils.FormatDateTime)
+			list[i].ModifyTimeStr = item.ModifyTime.Format(utils.FormatDateTime)
+
+			var contractDetail contractTable.ContractDetail
+			jsonErr := json.Unmarshal([]byte(list[i].ContractDetail), &contractDetail)
+			if jsonErr != nil {
+				this.FailWithMessage("获取审批列表失败!", "获取审批列表失败,Err:"+jsonErr.Error())
+				return
+			}
+			list[i].ContractInfo = contractDetail
+			//校验最新审批流数据
+			if contractApprovalRecord, has := contractApprovalRecordMap[item.ContractApprovalId]; has {
+				//审批流id
+				list[i].ContractApprovalRecordId = contractApprovalRecord.ContractApprovalRecordId
+				//审批时间
+				list[i].ApproveTime = contractApprovalRecord.ApproveTime
+				list[i].ApproveTimeStr = contractApprovalRecord.ApproveTime.Format(utils.FormatDateTime)
+				//判断是否有操作权限
+				list[i].Status = contractApprovalRecord.Status
+
+				//判断是否处于待审批状态
+				if contractApprovalRecord.Status == "待审批" {
+					//如果不是审批者,那么不能审批操作
+					if contractApprovalRecord.NodeType != "check" {
+						continue
+					}
+					//如果没有指定审批人的话
+					if contractApprovalRecord.ApproveUserId <= 0 {
+						//判断是否与审批角色一致,如果一致,那么允许操作审批
+						if contractApprovalRecord.ApproveRoleTypeCode == sysUser.RoleTypeCode {
+							list[i].CanApprovalButton = true
+						}
+					} else if contractApprovalRecord.ApproveUserId == sysUser.AdminId {
+						//如果指定审批人就是自己的话,那么操作审批
+						list[i].CanApprovalButton = true
+					}
+				}
+			}
+
+			//合同状态(突然说要改成展示合同状态,那就根据业务需求,改成返回:合同状态咯)
+			list[i].Status = item.ContractStatus
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	this.OkDetailed(contract.ContractApprovalListResp{
+		List:   list,
+		Paging: page,
+	}, "获取成功")
+}
+
+// @Title 驳回审批
+// @Description 驳回审批接口
+// @Param	request	body request.RejectReq true "type json string"
+// @Success Ret=200 驳回成功
+// @router /reject [post]
+func (this *ContractApprovalCommon) Reject() {
+	var req contractReq.RejectReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		this.FailWithMessage("参数解析异常!", "参数解析失败,Err:"+err.Error())
+		return
+	}
+	//审批流id编号
+	contractApprovalRecordId := req.ContractApprovalRecordId
+	if contractApprovalRecordId <= 0 {
+		this.FailWithMessage("请传入审批流编号!", "请传入审批流编号")
+		return
+	}
+
+	//获取审批流信息
+	contractApprovalRecord, err := contract_approval_record.GetContractApprovalRecordById(contractApprovalRecordId)
+	if err != nil {
+		this.FailWithMessage("获取审批流信息失败!", "获取审批流信息失败,Err:"+err.Error())
+		return
+	}
+	err = contractService.Reject(contractApprovalRecord, this.AdminWx, req.Remark)
+	if err != nil {
+		this.FailWithMessage("驳回审批失败!", "驳回审批失败,Err:"+err.Error())
+		return
+	}
+	this.OkWithMessage("驳回成功")
+}
+
+// @Title 处理审批单
+// @Description 处理审批单
+// @Param	request	body request.ApprovedReq true "type json string"
+// @Success Ret=200 申请成功
+// @router /approved [post]
+func (this *ContractApprovalCommon) Approved() {
+
+	var req contractReq.ApprovedReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		this.FailWithMessage("参数解析异常!", "参数解析失败,Err:"+err.Error())
+		return
+	}
+	//审批流id编号
+	contractApprovalRecordId := req.ContractApprovalRecordId
+	if contractApprovalRecordId <= 0 {
+		this.FailWithMessage("请传入审批流编号!", "请传入审批流编号")
+		return
+	}
+
+	//获取审批流信息
+	contractApprovalRecord, err := contract_approval_record.GetContractApprovalRecordById(contractApprovalRecordId)
+	if err != nil {
+		this.FailWithMessage("获取审批流信息失败!", "获取审批流信息失败,Err:"+err.Error())
+		return
+	}
+	err = contractService.Approved(contractApprovalRecord, this.AdminWx, req.Remark)
+	if err != nil {
+		this.FailWithMessage("审核失败!", "审核失败,Err:"+err.Error())
+		return
+	}
+	this.OkWithMessage("审核通过")
+}

+ 39 - 0
models/custom/contract/contract_service.go

@@ -0,0 +1,39 @@
+package contract
+
+import (
+	"hongze/hongze_mobile_admin/models/tables/contract_service_detail"
+	"rdluck_tools/orm"
+	"time"
+)
+
+//合同的服务内容
+type ContractService struct {
+	ContractServiceId int       `orm:"column(contract_service_id);pk"`
+	ContractId        int       `description:"合同id"`
+	ProductId         int       `description:"产品id,1:ficc;2:权益"`
+	ServiceTemplateId int       `description:"合同服务模板id"`
+	Title             string    `description:"套餐名称"`
+	Value             string    `description:"套餐的值"`
+	HasDetail         string    `description:"是否有详情,枚举值:是、否;默认:否"`
+	CreateTime        time.Time `description:"合同添加时间"`
+}
+
+//合同的服务内容
+type ContractServiceAndDetail struct {
+	ContractServiceId int    `orm:"column(contract_service_id);pk"`
+	ContractId        int    `description:"合同id"`
+	ProductId         int    `description:"产品id,1:ficc;2:权益"`
+	ServiceTemplateId int    `description:"合同服务模板id"`
+	Title             string `description:"套餐标题"`
+	Value             string `description:"套餐的值"`
+	HasDetail         string `description:"是否有详情,枚举值:是、否;默认:否"`
+	DetailList        []*contract_service_detail.ContractServiceDetail
+}
+
+//获取合同列表数据
+func GetContractServiceAndDetailList(contractId int) (list []*ContractServiceAndDetail, err error) {
+	o := orm.NewOrm()
+	sql := "select * from contract_service where contract_id = ?  "
+	_, err = o.Raw(sql, contractId).QueryRows(&list)
+	return
+}

+ 13 - 0
models/request/contract/contract.go

@@ -0,0 +1,13 @@
+package contract
+
+//套餐详情(自定义表格类数据)
+type AddContractServiceDetailReq struct {
+	CanEdit    bool   `json:"CanEdit";description:"是否可编辑"`
+	Type       string `json:"Type";description:"类型"`
+	Value      string `json:"Value"description:"类型"`
+	ValueId    []int  `json:"ValueId",description:"类型"`
+	HeadName   string `json:"HeadName",description:"表头名称"`
+	Tag        string `json:"Tag",description:"表头名称"`
+	RowDisable bool   `json:"RowDisable",description:"该行是否禁用操作"`
+	RowName    string `json:"RowName",description:"该行关联名称"`
+}

+ 15 - 0
models/request/contract/contract_approval.go

@@ -0,0 +1,15 @@
+package contract
+
+//驳回申请合同请求
+type RejectReq struct {
+	ContractApprovalRecordId int    `description:"审批流id"`
+	Remark                   string `description:"驳回理由"`
+}
+
+//通过申请合同请求
+type ApprovedReq struct {
+	ContractApprovalRecordId int    `description:"审批流id"`
+	Remark                   string `description:"驳回理由"`
+	NextApproval             bool   `description:"是否需要下级批准,预留字段,一期用不到,二期加入合规审批流程的时候,合规审批操作后可能会用到"`
+	NextApprovalUserId       int    `description:"是否指定下级审批人,预留字段,一期用不到,二期加入合规审批流程的时候,合规审批操作后可能会用到"`
+}

+ 24 - 1
models/response/contract/contract.go

@@ -1,7 +1,30 @@
 package contract
 
-import "hongze/hongze_mobile_admin/models/tables/company_contract"
+import (
+	"hongze/hongze_mobile_admin/models/tables/company_contract"
+	"hongze/hongze_mobile_admin/models/tables/contract_approval"
+	"rdluck_tools/paging"
+	"time"
+)
 
 type CompanyContractDetailResp struct {
 	*company_contract.CompanyContractDetail
 }
+
+type ContractOperationRecordListResp struct {
+	Id               int       ``
+	ContractId       int       `description:"合同id"`
+	Operation        string    `description:"操作类型,add:新增,edit:编辑,apply:发起审批,cancel_apply:撤回审批,reject:拒绝审批,approval:审批操作;长度36位"`
+	OpUserId         int       `description:"操作人id"`
+	OpUserName       string    `description:"操作人名称"`
+	Remark           string    `description:"备注,长度255位"`
+	ApprovalRecordId int       `orm:"column(approval_record_id)";description:"审批流id"`
+	CreateTime       time.Time `description:"日志添加时间"`
+	CreateTimeStr    string    `description:"日志添加时间"`
+}
+
+//审批列表
+type ContractApprovalListResp struct {
+	Paging *paging.PagingItem
+	List   []*contract_approval.ContractApprovalList `description:"列表数据"`
+}

+ 12 - 2
models/tables/approval_flow_node/approval_flow_node.go

@@ -38,8 +38,10 @@ type ApprovalFlowNodeList struct {
 
 //审批人、抄送人信息
 type User struct {
-	AdminId int    `description:"人员id(审批人、抄送人)"`
-	Name    string `description:"人员名称(审批人、抄送人)"`
+	AdminId      int    `description:"人员id(审批人、抄送人)"`
+	Name         string `description:"人员名称(审批人、抄送人)"`
+	RoleTypeCode string `description:"人员角色类型(审批人、抄送人)"`
+	Mobile       string `description:"手机号(审批人、抄送人)"`
 }
 
 //根据流程id获取整个审批流程节点路线
@@ -49,3 +51,11 @@ func GetListByFlowId(flowId, version int) (items []*ApprovalFlowNodeList, err er
 	_, err = o.Raw(sql, flowId, version).QueryRows(&items)
 	return
 }
+
+//根据节点id获取节点信息
+func GetByNodeId(nodeId int) (item *ApprovalFlowNode, err error) {
+	sql := `SELECT * FROM approval_flow_node WHERE node_id=? LIMIT 1 `
+	o := orm.NewOrm()
+	err = o.Raw(sql, nodeId).QueryRow(&item)
+	return
+}

+ 833 - 0
models/tables/company/company.go

@@ -0,0 +1,833 @@
+package company
+
+import (
+	"fmt"
+	"hongze/hongze_mobile_admin/utils"
+	"rdluck_tools/orm"
+	"rdluck_tools/paging"
+	"time"
+)
+
+type Company struct {
+	CompanyId       int       `orm:"column(company_id);pk"`
+	CompanyName     string    `description:"客户名称"`
+	CompanyType     int       `orm:"column(type)"`
+	CreditCode      string    `description:"社会统一信用码"`
+	CompanyCode     string    `description:"客户编码"`
+	Sort            int       `description:"优先级"`
+	IsFeeCustomer   int       `description:"是否付费用户"`
+	Country         string    `description:"国家编码"`
+	Province        string    `description:"省"`
+	City            string    `description:"市"`
+	Address         string    `description:"详细地址"`
+	Enabled         int       `description:"用户状态"`
+	CreatedTime     time.Time `description:"创建时间"`
+	LastUpdatedTime time.Time `description:"最后一次阅读时间"`
+	Seller          string    `description:"销售员"`
+	SellsId         int       `description:"销售员id"`
+	CompanyBelong   string    `description:"客户所属,ficc:ficc客户,public_offering:公募客户,partner:合作伙伴"`
+	StartDate       string    `description:"合同开始日期"`
+	EndDate         string    `description:"合同结束日期"`
+	LastType        int       `description:"原客户标签"`
+	IsVip           int       `description:"0:普通用户,1:大客户"`
+	FirstStartDate  string    `description:"首次设置为试用客户开始时间"`
+	FirstEndDate    string    `description:"首次设置为试用客户结束时间"`
+	DateType        int       `description:"设置流失类型,1:1个月,2:2个月,3:3个月"`
+	Remark          string    `description:"备注信息"`
+	RegionType      string    `description:"地区类型,国内,国外"`
+}
+
+//新增客户
+func AddCompany(item *Company) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+//根据企业id获取企业信息
+func GetCompanyById(companyId int) (items *Company, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM company where company_id= ? `
+	err = o.Raw(sql, companyId).QueryRow(&items)
+	return
+}
+
+type CompanySearchItem struct {
+	CompanyId       int    `orm:"column(company_id);pk"`
+	CompanyName     string `description:"客户名称"`
+	CreditCode      string `description:"社会统一信用码"`
+	CompanyCode     string `description:"客户编码"`
+	StartDate       string `description:"合同开始日期"`
+	EndDate         string `description:"合同结束日期"`
+	LoseReason      string `description:"流失原因"`
+	LossTime        string `description:"流失时间"`
+	Status          string `description:"客户状态:'试用','永续','冻结','流失','正式' "`
+	CompanyType     string `description:"客户类型:ficc/权益"`
+	ApproveStatus   string `description:"审批状态:'审批中','通过','驳回' 审批状态为空时,表示没有审批申请"`
+	SellerName      string `description:"销售:吉根龙/颖丹"`
+	SellerIds       string `description:"销售ID集合,包含ficc和权益的销售id";json:"-"`
+	Duration        string `description:"时长"`
+	FreezeTime      string `description:"冻结时间"`
+	GroupId         int    `description:"分组id"`
+	GroupIds        string `description:"分组id集合,包含ficc和权益的小组id";json:"-"`
+	DepartmentId    int    `description:"部门id"`
+	IsSuspend       int    `description:"是否暂停:1:暂停,0:启用 "`
+	ProductId       int    `description:"产品id"`
+	IsMoveShow      int    `description:"按钮显示控制:1:显示,移动按钮,0:不显示移动按钮"`
+	SellerId        int    `description:"销售id"`
+	BtnItem         *ButtonPermission
+	FormalTime      string `description:"转正时间"`
+	FreezeStartDate string `description:"冻结开始日期"`
+	FreezeEndDate   string `description:"冻结结束日期"`
+	RegionType      string `description:"地区类型,国内,国外"`
+}
+
+type CompanySearchListResp struct {
+	List []*CompanySearchItem
+}
+
+func GetCompanySearchList(condition string, pars []interface{}) (items []*CompanySearchItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT DISTINCT a.company_id,a.company_name,a.credit_code,a.company_code,a.region_type,
+            GROUP_CONCAT(b.status ORDER BY b.product_id ASC SEPARATOR '/') AS status,
+			GROUP_CONCAT(DISTINCT b.seller_name ORDER BY b.product_id ASC SEPARATOR '/' ) AS seller_name,
+			GROUP_CONCAT(DISTINCT b.seller_id ORDER BY b.product_id ASC SEPARATOR '/') AS seller_ids,
+			GROUP_CONCAT(DISTINCT b.group_id ORDER BY b.product_id ASC SEPARATOR '/' ) AS group_ids,
+			b.freeze_time,b.loss_time,a.lose_reason,b.is_suspend,b.seller_id,b.product_id,b.group_id,b.formal_time,b.freeze_time,
+			GROUP_CONCAT(b.start_date ORDER BY b.product_id ASC SEPARATOR '/') AS start_date,
+			GROUP_CONCAT(b.end_date ORDER BY b.product_id ASC SEPARATOR '/') AS end_date,
+			GROUP_CONCAT(DISTINCT b.company_type ORDER BY b.product_id ASC SEPARATOR '/') AS company_type,
+            GROUP_CONCAT(DISTINCT b.approve_status ORDER BY b.product_id ASC SEPARATOR '/') AS approve_status,
+            GROUP_CONCAT(DISTINCT b.freeze_start_date ORDER BY b.product_id ASC SEPARATOR '/') AS freeze_start_date,
+            GROUP_CONCAT(DISTINCT b.freeze_end_date ORDER BY b.product_id ASC SEPARATOR '/') AS freeze_end_date
+			FROM company AS a
+			INNER JOIN company_product AS b ON a.company_id=b.company_id
+			WHERE a.enabled=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.company_id ORDER BY  a.last_updated_time DESC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+type CompanyItem struct {
+	CompanyId        int    `orm:"column(company_id);pk"`
+	CompanyName      string `description:"客户名称"`
+	CreditCode       string `description:"社会统一信用码"`
+	CompanyCode      string `description:"客户编码"`
+	StartDate        string `description:"合同开始日期"`
+	EndDate          string `description:"合同结束日期"`
+	LoseReason       string `description:"流失原因"`
+	LossTime         string `description:"流失时间"`
+	Status           string `description:"客户状态:'试用','永续','冻结','流失','正式' "`
+	CompanyType      string `description:"客户类型:ficc/权益"`
+	ApproveStatus    string `description:"审批状态:'待审批','已审批','驳回' 审批状态为空时,表示没有审批申请"`
+	SellerName       string `description:"销售:吉根龙/颖丹"`
+	SellerId         int    `description:"销售ID"`
+	SellerIds        string `description:"销售ID集合,包含ficc和权益的销售id";json:"-"`
+	ExpireDay        string `description:"到期天数"`
+	FreezeTime       string `description:"冻结时间"`
+	GroupId          int    `description:"分组id"`
+	GroupIds         string `description:"分组id集合,包含ficc和权益的小组id";json:"-"`
+	DepartmentId     int    `description:"部门id"`
+	IndustryName     string `description:"所属行业"`
+	IsSuspend        int    `description:"是否暂停:1:暂停,0:启用 "`
+	CreatedTime      string `description:"创建时间"`
+	Source           string `description:"客户来源"`
+	Address          string `description:"详细地址"`
+	Reasons          string `description:"新增理由"`
+	FreezeStartDate  string `description:"冻结开始日期"`
+	FreezeEndDate    string `description:"冻结结束日期"`
+	FreezeExpireDays int    `description:"冻结到期天数"`
+	BtnItem          *ButtonPermission
+	ProductId        int    `json:"-"`
+	FormalTime       string `description:"转正时间"`
+	IsShared         bool   `description:"是否共享客户"`
+	RegionType       string `description:"区域:国内,海外"`
+}
+
+type CompanyListResp struct {
+	Paging      *paging.PagingItem
+	List        []*CompanyItem
+	StatusCount []*CompanyStatus
+}
+
+func GetCompanyListCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(DISTINCT b.company_id) AS count
+			FROM company AS a
+			INNER JOIN company_product AS b ON a.company_id=b.company_id
+			WHERE a.enabled=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetCompanyList(condition, status, sortStr string, pars []interface{}, startSize, pageSize int) (items []*CompanyItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.company_id,a.company_name,a.credit_code,a.company_code,a.created_time,a.address,a.region_type,b.group_id,
+			GROUP_CONCAT(b.status ORDER BY b.product_id ASC SEPARATOR '/') AS status,
+			GROUP_CONCAT(DISTINCT b.seller_name ORDER BY b.product_id ASC SEPARATOR '/') AS seller_name,
+			GROUP_CONCAT(DISTINCT b.seller_id ORDER BY b.product_id ASC SEPARATOR '/') AS seller_ids,
+			GROUP_CONCAT(DISTINCT b.industry_name ORDER BY b.product_id ASC SEPARATOR '/') AS industry_name, 
+			GROUP_CONCAT(DISTINCT b.product_name ORDER BY b.product_id ASC SEPARATOR '/') AS company_type,
+			GROUP_CONCAT(b.start_date ORDER BY b.product_id ASC SEPARATOR '/') AS start_date,
+			GROUP_CONCAT(b.end_date ORDER BY b.product_id ASC SEPARATOR '/') AS end_date,
+			GROUP_CONCAT(DISTINCT b.loss_time ORDER BY b.product_id ASC SEPARATOR '/') AS loss_time,
+			GROUP_CONCAT(DISTINCT b.lose_reason ORDER BY b.product_id ASC SEPARATOR '/') AS lose_reason,
+			GROUP_CONCAT(DISTINCT b.source ORDER BY b.product_id ASC SEPARATOR '/') AS source,
+			GROUP_CONCAT(DISTINCT b.reasons ORDER BY b.product_id ASC SEPARATOR '/') AS reasons,
+			GROUP_CONCAT(DISTINCT b.approve_status ORDER BY b.product_id ASC SEPARATOR '/') AS approve_status,
+			GROUP_CONCAT(DISTINCT b.group_id ORDER BY b.product_id ASC SEPARATOR '/') AS group_ids,
+			b.is_suspend,
+			b.freeze_start_date,
+			b.freeze_end_date,
+            b.seller_id,
+			b.product_id,
+            b.formal_time,
+            b.freeze_time
+			FROM company AS a
+			INNER JOIN company_product AS b ON a.company_id=b.company_id
+			WHERE a.enabled=1 `
+	if condition != "" {
+		sql += condition
+	}
+
+	//sortStr := ``
+	//如果没有传入排序字段,那么按照状态排序
+	if sortStr == "" {
+		if status == "全部" || status == utils.COMPANY_STATUS_TRY_OUT { //试用
+			sortStr = " ORDER BY  a.created_time DESC "
+		} else if status == utils.COMPANY_STATUS_FORMAL { //正式
+			sortStr = " ORDER BY  b.formal_time DESC "
+		} else if status == utils.COMPANY_STATUS_FREEZE { //冻结
+			sortStr = " ORDER BY  b.freeze_time DESC "
+		} else if status == utils.COMPANY_STATUS_LOSE { //流失
+			sortStr = " ORDER BY  b.loss_time DESC "
+		} else {
+			sortStr = " ORDER BY  a.created_time DESC "
+		}
+	}
+
+	sql += ` GROUP BY a.company_id ` + sortStr + `  LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type CompanyStatus struct {
+	Status string `description:"客户状态:'试用','永续','冻结','流失','正式' "`
+	Count  int    `description:"数量 "`
+}
+
+func GetCompanyStatusCount(condition string, pars []interface{}) (items []*CompanyStatus, err error) {
+	o := orm.NewOrm()
+	sql := `  SELECT b.status,COUNT(DISTINCT a.company_id) AS count FROM company AS a 
+             INNER JOIN company_product AS b ON a.company_id=b.company_id 
+			 WHERE a.enabled=1 AND b.status IN('正式','试用','冻结','永续') `
+	if condition != "" {
+		sql += condition
+	}
+	//sql += `GROUP BY b.status `
+	//_, err = o.Raw(sql, pars).QueryRows(&items)
+
+	sql += `GROUP BY b.status `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetCompanyStatusLoseCount(condition string, pars []interface{}) (items *CompanyStatus, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT b.status,COUNT(DISTINCT a.company_id) AS count FROM company AS a 
+INNER JOIN company_product AS b ON a.company_id=b.company_id 
+WHERE a.enabled=1 AND b.status='流失' `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&items)
+	return
+}
+
+func GetCompanyStatusFreezeCount(condition string, pars []interface{}) (items *CompanyStatus, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT b.status,COUNT(DISTINCT a.company_id) AS count FROM company AS a 
+			INNER JOIN company_product AS b ON a.company_id=b.company_id 
+			WHERE a.enabled=1 AND b.status='冻结' `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&items)
+	return
+}
+
+func GetCompanyStatusForverCount(condition string, pars []interface{}) (items *CompanyStatus, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT b.status,COUNT(DISTINCT a.company_id) AS count FROM company AS a 
+			INNER JOIN company_product AS b ON a.company_id=b.company_id 
+			WHERE a.enabled=1 AND b.status='永续' `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&items)
+	return
+}
+
+//新增客户请求参数
+type CompanyAddReq struct {
+	CompanyName   string `description:"客户名称"`
+	CreditCode    string `description:"社会统一信用码"`
+	CompanyCode   string `description:"客户编码"`
+	Province      string `description:"省"`
+	City          string `description:"市"`
+	Status        string `description:"客户状态,'试用','永续','冻结','流失','正式','潜在'"`
+	CompanyType   string `description:"客户类型,ficc/权益"`
+	IndustryId    int    `description:"所属行业id"`
+	Source        string `description:"来源"`
+	SellsId       int    `description:"销售员id"`
+	Reasons       string `description:"新增理由,备注"`
+	PermissionIds string `description:"权限id,多个用英文逗号隔开"`
+	RegionType    string `description:"地区类型,国内,海外"`
+	UserId        int    `description:"联系人ID"`
+}
+
+//新增客户请求参数
+type CompanyAddResp struct {
+	CompanyId int `description:"客户ID"`
+}
+
+func GetCompanyCode() (companyCode string, err error) {
+	var num int
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS num FROM company WHERE created_time >=? `
+	err = o.Raw(sql, time.Now().Format(utils.FormatDate)).QueryRow(&num)
+	if err != nil {
+		return
+	}
+	fmt.Println("num", num)
+	companyCode = "KH" + time.Now().Format("20060102") + fmt.Sprintf("%03d", num)
+	return
+}
+
+type CompanyDetail struct {
+	CompanyId   int    `orm:"column(company_id);pk"`
+	CompanyName string `description:"客户名称"`
+	CreditCode  string `description:"社会统一信用码"`
+	CompanyCode string `description:"客户编码"`
+	Province    string `description:"省"`
+	City        string `description:"市"`
+	Address     string `description:"详细地址"`
+	RegionType  string `description:"地区类型,1:国内,2:国外"`
+}
+
+func GetCompanyDetailById(companyId int) (item *CompanyDetail, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company WHERE company_id =? `
+	err = o.Raw(sql, companyId).QueryRow(&item)
+	return
+}
+
+func GetCompanyCountByCompanyName(companyName string) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS COUNT FROM company AS a WHERE a.company_name=? `
+	err = o.Raw(sql, companyName).QueryRow(&count)
+	return
+}
+
+func GetCompanyCountByCreditCode(creditCode string) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS COUNT FROM company AS a WHERE a.credit_code=? `
+	err = o.Raw(sql, creditCode).QueryRow(&count)
+	return
+}
+
+func GetCompanyCountByCompanyNameAndCompanyId(companyId int, companyName string) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS COUNT FROM company AS a WHERE a.company_id<>? AND a.company_name=? `
+	err = o.Raw(sql, companyId, companyName).QueryRow(&count)
+	return
+}
+
+func GetCompanyCountByCreditCodeAndCompanyId(companyId int, creditCode string) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS COUNT FROM company AS a WHERE a.company_id<>? AND a.credit_code=? `
+	err = o.Raw(sql, companyId, creditCode).QueryRow(&count)
+	return
+}
+
+//根据社会信用码获取客户信息
+func GetCompanyByCreditCode(creditCode string) (companyInfo *Company, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT *  FROM company AS a WHERE a.credit_code=? `
+	err = o.Raw(sql, creditCode).QueryRow(&companyInfo)
+	return
+}
+
+func GetCompanyTryOutCount(status string, sellerId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(DISTINCT a.company_id) AS count FROM company AS a 
+			INNER JOIN company_product AS b ON a.company_id=b.company_id
+			WHERE b.status=?
+			AND b.seller_id=? `
+	err = o.Raw(sql, status, sellerId).QueryRow(&count)
+	return
+}
+
+type CompanySearchResp struct {
+	List []*CompanyItem
+}
+
+func GetCompanyBySearch(keyWord string) (item []*CompanyItem, err error) {
+	o := orm.NewOrm()
+	var sql string
+	if keyWord == "##" {
+		sql = ` SELECT * FROM company ORDER BY RAND() LIMIT 5 `
+	} else {
+		sql = `SELECT * FROM company WHERE company_name LIKE '%` + keyWord + `%' LIMIT 5`
+	}
+	_, err = o.Raw(sql).QueryRows(&item)
+	return
+}
+
+//编辑客户请求参数
+type CompanyEditReq struct {
+	CompanyId   int                      `description:"客户id"`
+	CompanyName string                   `description:"客户名称"`
+	CreditCode  string                   `description:"社会统一信用码"`
+	Province    string                   `description:"省"`
+	City        string                   `description:"市"`
+	Products    []*CompanyProductEditReq `description:"产品信息"`
+}
+
+//编辑客户信息请求参数
+type CompanyInfoEditReq struct {
+	CompanyId    int    `description:"客户id"`
+	CompanyName  string `description:"客户名称"`
+	CreditCode   string `description:"社会统一信用码"`
+	Province     string `description:"省"`
+	City         string `description:"市"`
+	IndustryId   int    `description:"所属行业id"`
+	IndustryName string `description:"所属行业id"`
+	Source       string `description:"来源"`
+}
+
+type CompanyProductEditReq struct {
+	CompanyProductId int    `description:"客户产品id"`
+	Status           string `description:"客户状态,试用,永续"`
+	CompanyType      string `description:"客户类型,ficc/权益"`
+	IndustryId       int    `description:"所属行业id"`
+	Source           string `description:"来源"`
+	SellsId          int    `description:"销售员id"`
+	Reasons          string `description:"新增理由"`
+	PermissionIds    string `description:"权限id,多个用英文逗号隔开"`
+}
+
+type CompanyFuzzySearch struct {
+	CompanyId   int    `orm:"column(company_id);pk"`
+	CompanyName string `description:"客户名称"`
+	CreditCode  string `description:"社会统一信用码"`
+	CompanyCode string `description:"客户编码"`
+}
+
+//新增客户请求参数
+type CompanyFuzzySearchReq struct {
+	List []*CompanyFuzzySearch
+}
+
+func GetCompanyFuzzySearch(keyWord string) (items []*CompanyFuzzySearch, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company WHERE company_name LIKE '%` + keyWord + `%' `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+//修改客户
+func ModifyCompany(item *Company) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE  company
+			SET
+			  company_name = ?,
+			  credit_code = ?,
+			  address = ?,
+			  last_updated_time = NOW(),
+			  province = ?,
+			  city = ?
+			WHERE company_id =?`
+	_, err = o.Raw(sql, item.CompanyName, item.CreditCode, item.Address, item.Province, item.City, item.CompanyId).Exec()
+	return
+}
+
+//更新客户基础信息
+func (company *Company) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(company, cols...)
+	return
+}
+
+type DeletePptReq struct {
+	CompanyId int `description:"客户id"`
+}
+
+func DeleteCompanyById(companyId int) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+	sql := `DELETE FROM company_product WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	if err != nil {
+		return
+	}
+	sql = `DELETE FROM company_report_permission WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	if err != nil {
+		return
+	}
+
+	//联系人表
+	sql = `DELETE FROM wx_user WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	if err != nil {
+		return
+	}
+
+	//联系人 与 销售 关系表
+	sql = `DELETE FROM user_seller_relation WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	if err != nil {
+		return
+	}
+
+	//sql = `DELETE FROM company_operation_record WHERE company_id=? `
+	//_, err = o.Raw(sql, companyId).Exec()
+	//if err != nil {
+	//	return
+	//}
+
+	sql = `DELETE FROM company_contract WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = `DELETE FROM company_contract_permission WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = `DELETE FROM company WHERE company_id=? `
+	_, err = o.Raw(sql, companyId).Exec()
+	return
+}
+
+type CompanyReceiveItem struct {
+	CompanyId    int    `orm:"column(company_id);pk"`
+	CompanyName  string `description:"客户名称"`
+	StartDate    string `description:"合同开始日期"`
+	EndDate      string `description:"合同结束日期"`
+	Status       string `description:"客户状态:'试用','永续','冻结','流失','正式' "`
+	ProductName  string `description:"客户类型:ficc/权益"`
+	SellerName   string `description:"销售:吉根龙/颖丹"`
+	CreatedTime  string `description:"创建时间"`
+	ExpireDay    string `description:"到期天数"`
+	IndustryName string `description:"行业"`
+}
+
+type CompanyReceiveListResp struct {
+	Paging *paging.PagingItem
+	List   []*CompanyReceiveItem
+}
+
+func GetCompanyReceiveListCount(condition string, pars []interface{}, productId, receiveProductId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT 
+			COUNT(DISTINCT a.company_id ) AS COUNT
+			FROM company AS a
+			LEFT JOIN wx_user AS b ON a.company_id=b.company_id
+			LEFT JOIN company_product AS c ON a.company_id=c.company_id
+			WHERE a.enabled=1
+			AND c.product_id=? 
+            AND c.company_id NOT IN (SELECT company_id FROM company_product WHERE product_id=?) `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, receiveProductId, productId, pars).QueryRow(&count)
+	return
+}
+
+func GetCompanyReceiveList(condition string, pars []interface{}, productId, receiveProductId, startSize, pageSize int) (items []*CompanyReceiveItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT 
+			c.company_id,a.company_name,c.product_name,c.industry_name,c.seller_name,c.status,c.start_date,c.end_date,a.created_time
+			FROM company AS a
+			LEFT JOIN wx_user AS b ON a.company_id=b.company_id
+			LEFT JOIN company_product AS c ON a.company_id=c.company_id
+			WHERE a.enabled=1
+			AND c.product_id=? 
+            AND c.company_id NOT IN (SELECT company_id FROM company_product WHERE product_id=?) `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.company_id  ORDER BY  a.last_updated_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, receiveProductId, productId, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+//编辑客户请求参数
+type CompanyReceiveReq struct {
+	CompanyId     int    `description:"客户id"`
+	Status        string `description:"客户状态,试用,永续"`
+	IndustryId    int    `description:"所属行业id"`
+	Source        string `description:"来源"`
+	SellsId       int    `description:"销售员id,流失状态下直接领取,请传 0"`
+	Reasons       string `description:"新增理由"`
+	PermissionIds string `description:"权限id,多个用英文逗号隔开"`
+	CompanyType   string `description:"客户类型"`
+}
+
+//移动销售
+type MoveSellerReq struct {
+	CompanyId   int    `description:"客户id"`
+	SellsId     int    `description:"销售员id"`
+	CompanyType string `description:"客户类型"`
+}
+
+func GetExportCompanyIds(condition string, pars []interface{}) (company_id string, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT 
+			GROUP_CONCAT(DISTINCT b.company_id SEPARATOR ',') AS company_id
+			FROM company AS a
+			INNER JOIN company_product AS b ON a.company_id=b.company_id
+			LEFT JOIN wx_user AS c ON a.company_id=c.company_id
+			WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&company_id)
+	return
+}
+
+type ExportUser struct {
+	RealName       string
+	Mobile         string
+	Email          string
+	Position       string
+	IsMaker        int
+	DepartmentName string
+	CompanyId      int
+}
+
+func GetExportUser(companyId string) (items []*ExportUser, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.real_name,a.mobile,a.email,a.position,a.is_maker,department_name,a.company_id 
+			FROM wx_user AS a
+			WHERE a.company_id<>1 AND a.company_id IN(` + companyId + `)
+			ORDER BY a.company_id ASC `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type ExportCompanyProduct struct {
+	CompanyName  string
+	ProductId    int
+	ProductName  string
+	IndustryName string
+	SellerName   string
+	Status       string
+	StartDate    time.Time
+	EndDate      time.Time
+	CreateTime   string
+	Source       string
+	CreditCode   string
+	RegionType   string
+	LossTime     string
+}
+
+func GetExportCompanyProduct(companyId int) (items []*ExportCompanyProduct, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.company_name,b.product_id,b.product_name,b.industry_name,b.seller_name,b.status,b.start_date,b.end_date,b.create_time,
+             b.source,a.credit_code,a.region_type,b.loss_time
+			 FROM company AS a
+			 INNER JOIN company_product AS b ON a.company_id=b.company_id
+			 WHERE a.company_id=?
+			 ORDER BY b.product_id ASC `
+	_, err = o.Raw(sql, companyId).QueryRows(&items)
+	return
+}
+
+func GetExportPermissionName(companyId, productId int) (chart_permission_name string, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT GROUP_CONCAT(DISTINCT  c.permission_name ORDER BY sort ASC  SEPARATOR '/') AS chart_permission_name FROM company_product AS a
+			INNER JOIN company_report_permission AS b ON a.company_id=b.company_id AND a.product_id=b.product_id
+			INNER JOIN chart_permission AS c ON b.chart_permission_id=c.chart_permission_id AND b.product_id=c.product_id
+			WHERE a.company_id=? AND a.product_id=? `
+	err = o.Raw(sql, companyId, productId).QueryRow(&chart_permission_name)
+	return
+}
+
+//移动销售
+type SuspendReq struct {
+	CompanyId int `description:"客户id"`
+}
+
+func MoveCompanySeller(companyId, productId, sellerId, groupId, departmentId int, sellerName string) (err error) {
+	o := orm.NewOrm()
+	if sellerName != "" {
+		sql := `UPDATE company_product SET seller_id=?,seller_name=?,group_id=?,department_id=?,modify_time=NOW() WHERE company_id=? AND product_id=? `
+		_, err = o.Raw(sql, sellerId, sellerName, groupId, departmentId, companyId, productId).Exec()
+	} else {
+		sql := `UPDATE company_product SET seller_id=?,group_id=?,department_id=?,modify_time=NOW() WHERE company_id=? AND product_id=? `
+		_, err = o.Raw(sql, sellerId, groupId, departmentId, companyId, productId).Exec()
+	}
+	return
+}
+
+//新增客户请求参数
+type CheckCompanyInfoResp struct {
+	Status      int    `description:"检测状态:1:跳转到领取页面,0:不跳转,继续操作"`
+	CompanyId   int    `description:"客户id"`
+	ProductName string `description:"产品名称"`
+}
+
+type ButtonPermission struct {
+	BtnView         bool `description:"查看按钮权限:true显示,false不显示"`
+	BtnEdit         bool `description:"编辑按钮权限:true显示,false不显示"`
+	BtnDelete       bool `description:"删除按钮权限:true显示,false不显示"`
+	BtnSuspend      bool `description:"暂停/启用按钮权限:true显示,false不显示"`
+	BtnDelay        bool `description:"申请延期按钮权限:true显示,false不显示"`
+	BtnTurnPositive bool `description:"申请转正按钮权限:true显示,false不显示"`
+	BtnUpdate       bool `description:"服务更新按钮权限:true显示,false不显示"`
+	BtnAddAgreement bool `description:"补充协议按钮权限:true显示,false不显示"`
+	BtnThaw         bool `description:"申请解冻按钮权限:true显示,false不显示"`
+	BtnApplyReceive bool `description:"申请领取按钮权限:true显示,false不显示"`
+	BtnReceive      bool `description:"领取客户按钮权限:true显示,false不显示"`
+	BtnModifySeller bool `description:"修改销售按钮权限:true显示,false不显示"`
+	BtnReceiveOther bool `description:"领取其他部门客户按钮权限:true显示,false不显示"`
+	BtnFreeze       bool `description:"冻结按钮权限:true显示,false不显示"`
+	BtnTryOut       bool `description:"增开试用:true显示,false不显示"`
+	BtnDetail       bool `description:"详情按钮:true显示,false不显示"`
+}
+
+type ButtonSearchPermission struct {
+	BtnView         bool `description:"查看按钮权限:true显示,false不显示"`
+	BtnUpdate       bool `description:"服务更新按钮权限:true显示,false不显示"`
+	BtnEdit         bool `description:"编辑按钮权限:true显示,false不显示"`
+	BtnDelete       bool `description:"删除按钮权限:true显示,false不显示"`
+	BtnSuspend      bool `description:"暂停/启用按钮权限:true显示,false不显示"`
+	BtnDelay        bool `description:"申请延期按钮权限:true显示,false不显示"`
+	BtnTurnPositive bool `description:"申请转正按钮权限:true显示,false不显示"`
+	BtnThaw         bool `description:"申请解冻按钮权限:true显示,false不显示"`
+	BtnApplyReceive bool `description:"申请领取按钮权限:true显示,false不显示"`
+	BtnReceive      bool `description:"领取客户按钮权限:true显示,false不显示"`
+	BtnModifySeller bool `description:"修改销售按钮权限:true显示,false不显示"`
+	BtnReceiveOther bool `description:"领取其他部门客户按钮权限:true显示,false不显示"`
+}
+
+type ButtonDetailPermission struct {
+	BtnEdit        bool `description:"编辑按钮权限:true显示,false不显示"`
+	BtnDelete      bool `description:"删除按钮权限:true显示,false不显示"`
+	BtnHistoryList bool `description:"历史签约按钮权限:true显示,false不显示"`
+
+	BaseInfoEdit bool `description:"客户基础信息编辑操作:true可操作,false不可操作"`
+	FiccEdit     bool `description:"ficc类型,编辑按钮权限:true显示,false不显示"`
+	FiccDelete   bool `description:"ficc类型,删除按钮权限:true显示,false不显示"`
+	RaiEdit      bool `description:"权益类型,编辑按钮权限:true显示,false不显示"`
+	RaiDelete    bool `description:"权益类型,删除按钮权限:true显示,false不显示"`
+}
+
+func GetCompanyListExport(condition string, pars []interface{}) (items []*CompanyItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.company_id,a.company_name,a.credit_code,a.company_code,a.created_time,a.address,
+			GROUP_CONCAT(b.status ORDER BY b.product_id ASC SEPARATOR '/') AS status,
+			GROUP_CONCAT(DISTINCT b.seller_name ORDER BY b.product_id ASC SEPARATOR '/') AS seller_name,
+			GROUP_CONCAT(DISTINCT b.industry_name ORDER BY b.product_id ASC SEPARATOR '/') AS industry_name, 
+			GROUP_CONCAT(DISTINCT b.product_name ORDER BY b.product_id ASC SEPARATOR '/') AS company_type,
+			GROUP_CONCAT(b.start_date ORDER BY b.product_id ASC SEPARATOR '/') AS start_date,
+			GROUP_CONCAT(b.end_date ORDER BY b.product_id ASC SEPARATOR '/') AS end_date,
+			GROUP_CONCAT(DISTINCT b.loss_time ORDER BY b.product_id ASC SEPARATOR '/') AS loss_time,
+			GROUP_CONCAT(DISTINCT b.lose_reason ORDER BY b.product_id ASC SEPARATOR '/') AS lose_reason,
+			GROUP_CONCAT(DISTINCT b.source ORDER BY b.product_id ASC SEPARATOR '/') AS source,
+			GROUP_CONCAT(DISTINCT b.reasons ORDER BY b.product_id ASC SEPARATOR '/') AS reasons,
+			GROUP_CONCAT(DISTINCT b.approve_status ORDER BY b.product_id ASC SEPARATOR '/') AS approve_status,
+			GROUP_CONCAT(DISTINCT b.group_id ORDER BY b.product_id ASC SEPARATOR '/') AS group_id,
+			b.is_suspend,
+			b.freeze_start_date,
+			b.freeze_end_date,
+            b.seller_id,
+			b.product_id
+			FROM company AS a
+			INNER JOIN company_product AS b ON a.company_id=b.company_id
+			WHERE a.enabled=1 AND a.company_id<>1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.company_id  ORDER BY  a.last_updated_time DESC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+//冻结客户请求参数
+type CompanyFreezeReq struct {
+	CompanyId   int    `description:"客户id"`
+	CompanyType string `description:"客户类型:ficc/权益"`
+	Remark      string `description:"备注"`
+}
+
+//冻结客户
+func CompanyFreeze(companyId, productId int) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	freezeStartDate := time.Now().Format(utils.FormatDate)
+	freezeEndDate := time.Now().AddDate(0, 3, 0).Format(utils.FormatDate)
+
+	//修改用户产品状态
+	sql := `UPDATE company_product SET status='冻结',freeze_time=NOW(),modify_time=NOW(),start_date=?,end_date=?,freeze_start_date=?,freeze_end_date=? WHERE company_id=? AND product_id=? `
+	_, err = o.Raw(sql, freezeStartDate, freezeEndDate, freezeStartDate, freezeEndDate, companyId, productId).Exec()
+	if err != nil {
+		return err
+	}
+
+	//修改产品权限状态为关闭
+	sql = `UPDATE company_report_permission SET status='关闭',start_date=?,end_date=?,modify_time=NOW() WHERE company_id= ? AND product_id = ? `
+	_, err = o.Raw(sql, freezeStartDate, freezeEndDate, companyId, productId).Exec()
+	if err != nil {
+		return err
+	}
+
+	//修改客户为关闭
+	sql = `UPDATE company SET type=3,last_updated_time=NOW(),start_date=?,end_date=? WHERE company_id=? `
+	_, err = o.Raw(sql, freezeStartDate, freezeEndDate, companyId).Exec()
+	if err != nil {
+		return err
+	}
+	return
+}
+
+func ModifyCompanyGroupId(sellerId, groupId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_product SET group_id=? WHERE seller_id=? `
+	_, err = o.Raw(sql, groupId, sellerId).Exec()
+	return
+}
+
+func ModifyCompanyDepartmentId(sellerId, departmentId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_product SET department_id=? WHERE seller_id=? `
+	_, err = o.Raw(sql, departmentId, sellerId).Exec()
+	return
+}

+ 4 - 3
models/tables/company_approval_message/company_approval_message.go

@@ -31,10 +31,11 @@ func AddCompanyApprovalMessage(item *CompanyApprovalMessage) (err error) {
 }
 
 //修改审批消息
-func ModifyCompanyApprovalMessage(companyApprovalId int) (err error) {
+func ModifyCompanyApprovalMessage(companyApprovalId, sourceType int) (err error) {
 	o := orm.NewOrm()
-	sql := `UPDATE company_approval_message SET operation_status=2,modify_time=NOW() WHERE company_approval_id=? AND message_type=1 AND operation_status=1 `
-	_, err = o.Raw(sql, companyApprovalId).Exec()
+	sql := `UPDATE company_approval_message SET operation_status=2,modify_time=NOW() 
+WHERE company_approval_id=? AND source_type=?  AND message_type=1 AND operation_status=1 `
+	_, err = o.Raw(sql, companyApprovalId, sourceType).Exec()
 	return
 }
 

+ 46 - 0
models/tables/company_log/company_log.go

@@ -0,0 +1,46 @@
+package company_log
+
+import (
+	"rdluck_tools/orm"
+	"time"
+)
+
+type CompanyLog struct {
+	Id              int       `orm:"column(id);pk"`
+	CompanyId       int       `description:"客户id"`
+	CompanyName     string    `description:"客户名称"`
+	CreditCode      string    `description:"统一社会信用码"`
+	CompanyCode     string    `description:"客户编码"`
+	Sort            int       `description:"优先级"`
+	IsFeeCustomer   int       `description:"是否付费用户"`
+	Country         string    `description:"国家编码"`
+	ProvinceId      int       `description:"省id"`
+	CityId          int       `description:"市id"`
+	Address         string    `description:"详细地址"`
+	Ctype           int       `orm:"column(type)" description:"客户标签,1:付费客户,2:试用客户,3:流失客户,4:潜在客户" `
+	Enabled         int       `description:"用户状态"`
+	CreatedTime     time.Time `description:"创建时间"`
+	LastUpdatedTime time.Time `description:"最后修改时间"`
+	Seller          string    `description:"销售员"`
+	SellsId         int       `description:"销售员ID"`
+	CompanyBelong   string    `description:"客户所属,ficc:ficc客户,public_offering:公募客户,partner:合作伙伴"`
+	StartDate       string    `description:"合同开始日期"`
+	EndDate         string    `description:"合同结束日期"`
+	LoseReason      string    `description:"流失原因"`
+	LastType        int       `description:"原客户标签"`
+	FirstStartDate  string    `description:"首次设置为试用客户开始时间"`
+	FirstEndDate    string    `description:"首次设置为试用客户结束时间"`
+	DateType        int       `description:"日期类型"`
+	AdminId         int       `description:"操作人id"`
+	LogType         string    `description:"日志类型"`
+	LogCreateTime   time.Time `description:"日志创建时间"`
+	Province        string    `description:"省"`
+	City            string    `description:"市"`
+}
+
+//新增客户产品
+func AddCompanyLog(item *CompanyLog) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(item)
+	return
+}

+ 362 - 0
models/tables/contract/contract.go

@@ -0,0 +1,362 @@
+package contract
+
+import (
+	"fmt"
+	"hongze/hongze_mobile_admin/models/custom/contract"
+	"hongze/hongze_mobile_admin/utils"
+	"rdluck_tools/orm"
+	"time"
+)
+
+//合同
+type Contract struct {
+	ContractId       int       `orm:"column(contract_id);pk"`
+	ContractCode     string    `description:"合同编号,长度32位"`
+	SellerId         int       `description:"所属销售id"`
+	SellerName       string    `description:"所属销售名称"`
+	ProductId        int       `description:"产品id,1:ficc;2:权益"`
+	ContractType     string    `description:"合同类型,枚举值:'新签合同','续约合同','补充协议'"`
+	Status           string    `description:"合同状态,枚举值:'待提交','待审批','已撤回','已审批','已驳回','已作废'"`
+	StartDate        time.Time `description:"合同开始日期"`
+	EndDate          time.Time `description:"合同结束日期"`
+	OriginalPrice    float64   `description:"合同原金额,优惠前的金额"`
+	Price            float64   `description:"实际金额,优惠后的金额"`
+	PayRemark        string    `description:"付款方式说明,长度255位"`
+	CompanyName      string    `description:"客户名称,甲方名称,长度32位"`
+	CreditCode       string    `description:"社会统一信用代码,长度64位"`
+	ProvinceId       int       `description:"省级id"`
+	Province         string    `description:"省级名称,长度16位"`
+	CityId           int       `description:"市级id"`
+	City             string    `description:"市级名称,长度32位"`
+	Address          string    `description:"详细地址"`
+	Fax              string    `description:"传真,长度32位"`
+	Phone            string    `description:"电话,长度32位"`
+	Postcode         string    `description:"邮编,长度16位"`
+	Remark           string    `description:"补充内容,长度255位"`
+	ModifyContent    string    `description:"修改内容"`
+	ApprovalRemark   string    `description:"审核备注"`
+	FileUrl          string    `description:"合同文件地址"`
+	CheckBackFileUrl string    `description:"签回合同文件地址"`
+	TemplateId       int       `description:"模板id"`
+	SourceId         int       `description:"来源合同id,默认是0;如果是通过其他合同复制过来的,那么就是原合同的id"`
+	IsDelete         int       `description:"是否已经删除,0:未删除,1:已删除",json:"-"`
+	ModifyTime       time.Time `description:"合同最近一次修改时间"`
+	CreateTime       time.Time `description:"合同添加时间"`
+}
+
+//根据合同id获取合同信息
+func GetContractById(contractId int) (contractInfo *Contract, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract where contract_id = ? `
+	err = o.Raw(sql, contractId).QueryRow(&contractInfo)
+	return
+}
+
+//合同详情信息(包含服务信息等)
+type ContractDetail struct {
+	ContractId     int       `description:"合同唯一id"`
+	ContractCode   string    `description:"合同编号,长度32位"`
+	SellerId       int       `description:"所属销售id"`
+	SellerName     string    `description:"所属销售名称"`
+	ProductId      int       `description:"产品id,1:ficc;2:权益"`
+	ContractType   string    `description:"合同类型,枚举值:'新签合同','续约合同','补充协议'"`
+	Status         string    `description:"合同状态,枚举值:'待提交','待审批','已撤回','已审批','已驳回','已作废'"`
+	StartDate      time.Time `description:"合同开始日期"`
+	EndDate        time.Time `description:"合同结束日期"`
+	OriginalPrice  float64   `description:"合同原金额,优惠前的金额"`
+	Price          float64   `description:"实际金额,优惠后的金额"`
+	PayRemark      string    `description:"付款方式说明,长度255位"`
+	CompanyName    string    `description:"客户名称,甲方名称,长度32位"`
+	CreditCode     string    `description:"社会统一信用代码,长度64位"`
+	ProvinceId     int       `description:"省级id"`
+	Province       string    `description:"省级名称,长度16位"`
+	CityId         int       `description:"市级id"`
+	City           string    `description:"市级名称,长度32位"`
+	Address        string    `description:"详细地址"`
+	Fax            string    `description:"传真,长度32位"`
+	Phone          string    `description:"电话,长度32位"`
+	Postcode       string    `description:"邮编,长度16位"`
+	Remark         string    `description:"补充内容,长度255位"`
+	ModifyContent  string    `description:"修改内容"`
+	ApprovalRemark string    `description:"审核备注"`
+	FileUrl        string    `description:"合同文件地址"`
+	TemplateId     int       `description:"模板id"`
+	SourceId       int       `description:"来源合同id,默认是0;如果是通过其他合同复制过来的,那么就是原合同的id"`
+	IsDelete       int       `json:"-";description:"是否已经删除,0:未删除,1:已删除"`
+	ModifyTime     time.Time `description:"合同最近一次修改时间"`
+	CreateTime     time.Time `description:"合同添加时间"`
+
+	StartDateStr  string `description:"合同起始时间"`
+	EndDateStr    string `description:"合同结束时间"`
+	ModifyTimeStr string `description:"最近一次更新时间"`
+	CreateTimeStr string `description:"合同添加时间"`
+	Service       []*contract.ContractServiceAndDetail
+}
+
+//根据合同id获取合同详情信息
+func GetContractDetailById(contractId int) (contractInfo *ContractDetail, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract where contract_id = ? `
+	err = o.Raw(sql, contractId).QueryRow(&contractInfo)
+	return
+}
+
+//合同添加
+func AddContract(contractInfo *Contract, contractServiceAndDetailList []*contract.ContractServiceAndDetail) (newContract *Contract, err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	//合同数据入库
+	contractId, err := o.Insert(contractInfo)
+	if err != nil {
+		return
+	}
+	contractInfo.ContractId = int(contractId)
+	for i := 0; i < len(contractServiceAndDetailList); i++ {
+		//合同服务数据入库
+		tmpContractService := contractServiceAndDetailList[i]
+		contractService := &contract.ContractService{
+			ContractId:        int(contractId),
+			ProductId:         contractInfo.ProductId,
+			ServiceTemplateId: tmpContractService.ServiceTemplateId,
+			Title:             tmpContractService.Title,
+			Value:             tmpContractService.Value,
+			HasDetail:         tmpContractService.HasDetail,
+			CreateTime:        time.Now(),
+		}
+		contractServiceId, serviceErr := o.Insert(contractService)
+		if serviceErr != nil {
+			err = serviceErr
+			return
+		}
+		contractService.ContractServiceId = int(contractServiceId)
+
+		//合同服务详情入库
+		for j := 0; j < len(tmpContractService.DetailList); j++ {
+			contractServiceDetail := tmpContractService.DetailList[j]
+			//合同服务编号
+			contractServiceDetail.ContractServiceId = contractService.ContractServiceId
+			contractServiceDetail.ContractId = int(contractId)
+			contractServiceDetail.ServiceTemplateId = contractService.ServiceTemplateId
+
+			//合同服务详情入库
+			contractServiceDetailId, detailErr := o.Insert(contractServiceDetail)
+			if detailErr != nil {
+				err = detailErr
+				return
+			}
+			contractServiceDetail.Id = int(contractServiceDetailId)
+			tmpContractService.DetailList[j] = contractServiceDetail
+		}
+	}
+	newContract = contractInfo
+	return
+}
+
+type ContractList struct {
+	ContractId     int       `description:"合同唯一id"`
+	ContractCode   string    `description:"合同编号,长度32位"`
+	SellerId       int       `description:"所属销售id"`
+	SellerName     string    `description:"所属销售名称"`
+	ProductId      int       `description:"产品id,1:ficc;2:权益"`
+	ContractType   string    `description:"合同类型,枚举值:'新签合同','续约合同','补充协议'"`
+	Status         string    `description:"合同状态,枚举值:'待提交','待审批','已撤回','已审批','已驳回','已作废'"`
+	StartDate      time.Time `json:"-";description:"合同开始日期"`
+	EndDate        time.Time `json:"-";description:"合同结束日期"`
+	OriginalPrice  float64   `description:"合同原金额,优惠前的金额"`
+	Price          float64   `description:"实际金额,优惠后的金额"`
+	PayRemark      string    `description:"付款方式说明,长度255位"`
+	CompanyName    string    `description:"客户名称,甲方名称,长度32位"`
+	CreditCode     string    `description:"社会统一信用代码,长度64位"`
+	ProvinceId     int       `description:"省级id"`
+	Province       string    `description:"省级名称,长度16位"`
+	CityId         int       `description:"市级id"`
+	City           string    `description:"市级名称,长度32位"`
+	Address        string    `description:"详细地址"`
+	Fax            string    `description:"传真,长度32位"`
+	Phone          string    `description:"电话,长度32位"`
+	Postcode       string    `description:"邮编,长度16位"`
+	Remark         string    `json:"-";description:"补充内容,长度255位"`
+	ApprovalRemark string    `description:"审核备注"`
+	ModifyContent  string    `description:"修改内容"`
+	FileUrl        string    `description:"合同文件地址"`
+	TemplateId     int       `description:"模板id"`
+	SourceId       int       `description:"来源合同id,默认是0;如果是通过其他合同复制过来的,那么就是原合同的id"`
+	IsDelete       int       `json:"-";description:"是否已经删除,0:未删除,1:已删除"`
+	ModifyTime     time.Time `json:"-";description:"合同最近一次修改时间"`
+	CreateTime     time.Time `json:"-";description:"合同添加时间"`
+
+	StartDateStr  string `description:"合同起始时间"`
+	EndDateStr    string `description:"合同结束时间"`
+	ModifyTimeStr string `description:"最近一次更新时间"`
+	CreateTimeStr string `description:"合同添加时间"`
+	Service       []*contract.ContractServiceAndDetail
+}
+
+//获取合同列表数据数量
+func GetContractListCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := "select count(*) AS COUNT from contract where 1=1 AND is_delete = 0 "
+	sql += condition
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+//获取合同列表数据
+func GetContractList(condition string, pars []interface{}, startSize, pageSize int) (list []*ContractList, err error) {
+	o := orm.NewOrm()
+	sql := "select * from contract where 1=1 AND is_delete = 0 "
+	sql += condition
+	sql += ` order by modify_time desc LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+//修改合同
+func EditContract(contractInfo *Contract, contractServiceAndDetailList []*contract.ContractServiceAndDetail) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	contractId := contractInfo.ContractId
+	//合同数据入库
+	_, err = o.Update(contractInfo)
+	if err != nil {
+		return
+	}
+
+	//删除合同的原始服务信息
+	sql := `delete from contract_service where contract_id = ?`
+	_, err = o.Raw(sql, contractId).Exec()
+	//删除合同的原始服务详情信息
+	sql = `delete from contract_service_detail where contract_id = ?`
+	_, err = o.Raw(sql, contractId).Exec()
+
+	for i := 0; i < len(contractServiceAndDetailList); i++ {
+		//合同服务数据入库
+		tmpContractService := contractServiceAndDetailList[i]
+		contractService := &contract.ContractService{
+			ContractId:        int(contractId),
+			Title:             tmpContractService.Title,
+			ProductId:         contractInfo.ProductId,
+			ServiceTemplateId: tmpContractService.ServiceTemplateId,
+			Value:             tmpContractService.Value,
+			HasDetail:         tmpContractService.HasDetail,
+			CreateTime:        time.Now(),
+		}
+		contractServiceId, serviceErr := o.Insert(contractService)
+		if serviceErr != nil {
+			err = serviceErr
+			return
+		}
+		contractService.ContractServiceId = int(contractServiceId)
+
+		//合同服务详情入库
+		for j := 0; j < len(tmpContractService.DetailList); j++ {
+			contractServiceDetail := tmpContractService.DetailList[j]
+			//合同服务编号
+			contractServiceDetail.ContractServiceId = contractService.ContractServiceId
+			contractServiceDetail.ContractId = contractService.ContractId
+			contractServiceDetail.ServiceTemplateId = contractService.ServiceTemplateId
+
+			//合同服务详情入库
+			contractServiceDetailId, detailErr := o.Insert(contractServiceDetail)
+			if detailErr != nil {
+				err = detailErr
+				return
+			}
+			contractServiceDetail.Id = int(contractServiceDetailId)
+			tmpContractService.DetailList[j] = contractServiceDetail
+		}
+	}
+	return
+}
+
+//添加生成后的合同地址
+func AddContractPdf(contractId int, pdfUrl string) (err error) {
+	o := orm.NewOrm()
+
+	//删除合同的原始服务信息
+	sql := `update contract set file_url=? where contract_id = ?`
+	_, err = o.Raw(sql, pdfUrl, contractId).Exec()
+	return
+}
+
+//删除合同
+func DeleteContract(contractInfo *Contract) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	contractInfo.IsDelete = 1
+	contractInfo.ModifyTime = time.Now()
+	//合同数据入库
+	_, err = o.Update(contractInfo)
+	if err != nil {
+		return
+	}
+	return
+}
+
+//作废合同
+func InvalidContract(contractInfo *Contract) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	contractInfo.Status = "已作废"
+	contractInfo.ModifyTime = time.Now()
+	//合同数据入库
+	_, err = o.Update(contractInfo)
+	if err != nil {
+		return
+	}
+	return
+}
+
+//生成合同编号
+func GetCompanyContractCode(productId int) (companyCode string, err error) {
+	var num int
+	o := orm.NewOrm()
+	today := utils.GetToday(utils.FormatDate)
+	sql := `SELECT COUNT(1) AS num FROM contract where create_time>=?`
+	err = o.Raw(sql, today).QueryRow(&num)
+	if err != nil {
+		return
+	}
+	companyType := ""
+	switch productId {
+	case 1:
+		companyType = "FICC"
+	case 2:
+		companyType = "EQ"
+	}
+
+	companyCode = "HZ" + companyType + time.Now().Format("20060102") + fmt.Sprintf("%03d", num)
+	return
+}

+ 501 - 0
models/tables/contract_approval/contract_approval.go

@@ -0,0 +1,501 @@
+package contract_approval
+
+import (
+	"hongze/hongze_mobile_admin/models/tables/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract_approval_record"
+	"hongze/hongze_mobile_admin/utils"
+	"rdluck_tools/orm"
+	"time"
+)
+
+//合同审批单
+type ContractApproval struct {
+	ContractApprovalId int       `orm:"column(contract_approval_id);pk"`
+	ContractId         int       `orm:"column(contract_id)";description:"合同id"`
+	Status             string    `description:"审批单状态,枚举值:待审批','已审批','已驳回','已撤回',默认待审批"`
+	ApplyContent       string    `description:"待审内容,长度255位"`
+	ContractDetail     string    `description:"审批单详情,完整的合同json数据"`
+	ApplyUserId        int       `description:"申请人id"`
+	ApplyUserName      string    `description:"申请人名称"`
+	ApproveRemark      string    `description:"审批人备注,可以是驳回理由,长度128位"`
+	FlowId             int       `description:"审批流程id"`
+	FlowVersion        int       `description:"审批流程版本"`
+	CurrNodeId         int       `description:"当前审批节点id"`
+	ModifyTime         time.Time `description:"发起申请时间"`
+	CreateTime         time.Time `description:"最近一次审批单修改时间"`
+}
+
+//根据合同审批单id获取合同审批单信息
+func GetContractApprovalById(contractApprovalId int) (contractApprovalInfo *ContractApproval, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_approval where contract_approval_id = ? `
+	err = o.Raw(sql, contractApprovalId).QueryRow(&contractApprovalInfo)
+	return
+}
+
+//根据合同id获取最近一次待审批的合同审批单信息
+func GetLastPendingContractApprovalByContractId(contractId int) (contractApprovalInfo *ContractApproval, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_approval where status = "待审批" AND contract_id=? `
+	err = o.Raw(sql, contractId).QueryRow(&contractApprovalInfo)
+	return
+}
+
+//根据合同id获取总共被驳回的次数
+func GetRejectContractCountByContractId(contractId int) (total int64, err error) {
+	o := orm.NewOrm()
+	sql := `select count(1) total from contract_approval where status = "已驳回" AND contract_id=? `
+	err = o.Raw(sql, contractId).QueryRow(&total)
+	return
+}
+
+//获取审批列表数据数量
+func GetContractApprovalListCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `
+SELECT a.* from contract_approval a
+	JOIN contract c ON a.contract_id = c.contract_id 
+WHERE
+	c.is_delete = 0  `
+	sql += condition
+	sql += ` GROUP BY a.contract_id `
+	sql = `select count(*) count from (` + sql + `) b`
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+//审批列表
+type ContractApprovalList struct {
+	ContractApprovalId       int                     `description:"审批单id"`
+	ContractId               int                     `description:"合同id"`
+	ContractApprovalRecordId int                     `description:"审批流id"`
+	ContractCode             string                  `description:"合同编号"`
+	Status                   string                  `description:"审批单状态,枚举值:待审批','已审批','已驳回','已撤回',默认待审批"`
+	ContractStatus           string                  `json:"-";description:"合同状态,枚举值:待审批','已审批','已驳回','已撤回',默认待审批"`
+	ApplyContent             string                  `description:"待审内容"`
+	ContractDetail           string                  `json:"-";description:"提交审批时的合同信息;合同快照"`
+	ContractInfo             contract.ContractDetail `description:"提交审批时的合同信息;合同快照"`
+	ApproveRemark            string                  `description:"审核备注"`
+	ContractType             string                  `description:"合同类型"`
+	Price                    float64                 `description:"合同金额"`
+	CompanyName              string                  `description:"客户名称"`
+	FileUrl                  string                  `description:"合同下载地址"`
+	SellerId                 int                     `description:"销售id"`
+	ProductId                int                     `description:"产品id,1:ficc;2:权益"`
+	SellerName               string                  `description:"销售名称"`
+	StartDate                time.Time               `description:"合同开始日期"`
+	EndDate                  time.Time               `description:"合同结束日期"`
+	CreateTime               time.Time               `description:"发起审批的时间"`
+	ModifyTime               time.Time               `description:"最后一次修改的时间"`
+	ApproveTime              time.Time               `description:"审批时间"`
+	CanApprovalButton        bool                    `description:"是否具有审核操作权限,true才允许审核操作"`
+	StartDateStr             string                  `description:"合同开始日期(字符串类型)"`
+	EndDateStr               string                  `description:"合同结束日期(字符串类型)"`
+	CreateTimeStr            string                  `description:"发起审批的时间(字符串类型)"`
+	ModifyTimeStr            string                  `description:"最后一次修改的时间(字符串类型)"`
+	ApproveTimeStr           string                  `description:"审批时间(字符串类型)"`
+}
+
+//获取审批列表数据
+func GetContractApprovalList(childCondition, condition string, childPars, pars []interface{}, startSize, pageSize int) (list []*ContractApprovalList, err error) {
+	o := orm.NewOrm()
+	sql := `
+SELECT
+a.contract_approval_id,a.contract_id,a.apply_content,a.contract_detail,a.approve_remark,a.create_time,a.modify_time,a.status,c.status contract_status,c.product_id,
+c.contract_type,c.start_date,c.end_date,c.price,c.company_name,c.seller_id,c.seller_name,c.file_url,c.contract_code from contract_approval a
+	join 
+	( SELECT max( contract_approval_id ) max_id,contract_id FROM contract_approval where 1=1 `
+	sql += childCondition
+	sql += `  GROUP BY contract_id ) b on a.contract_approval_id=b.max_id
+	JOIN contract c ON a.contract_id = c.contract_id 
+WHERE c.is_delete = 0 `
+	sql += condition
+	sql += ` group by contract_id order by modify_time desc LIMIT ?,? `
+	_, err = o.Raw(sql, childPars, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+//根据合同id获取合同审批单信息
+func (ContractApproval) CheckPendingByContractId(contractId int) (has bool, err error) {
+	o := orm.NewOrm()
+	var contractApprovalInfo ContractApproval
+	sql := `select * from contract_approval where contract_id = ? AND status="待审批" limit 1`
+	err = o.Raw(sql, contractId).QueryRow(&contractApprovalInfo)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			//如果不存在待审批记录,那么报错信息置空,存在与否直接返回false
+			err = nil
+			return
+		}
+		//系统异常,返回错误信息
+		return
+	}
+	//存在待审批的审批单
+	has = true
+	return
+}
+
+//发起审批
+func (ContractApproval) Apply(contractApprovalInfo *ContractApproval, contractApprovalRecord *contract_approval_record.ContractApprovalRecord) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+	//审批单记录
+	ContractApprovalId, err := o.Insert(contractApprovalInfo)
+	if err != nil {
+		return
+	}
+	//审批单id
+	contractApprovalInfo.ContractApprovalId = int(ContractApprovalId)
+
+	//审批流记录
+	contractApprovalRecord.ContractApprovalId = contractApprovalInfo.ContractApprovalId
+	contractApprovalRecordId, err := o.Insert(contractApprovalRecord)
+	if err != nil {
+		return
+	}
+	//审批流id
+	contractApprovalRecord.ContractApprovalRecordId = int(contractApprovalRecordId)
+
+	//修改合同状态为待审批
+	sql := `UPDATE contract SET status="待审批",modify_time=NOW() WHERE contract_id = ?`
+	_, err = o.Raw(sql, contractApprovalInfo.ContractId).Exec()
+	return
+}
+
+//发起审批(工作流)
+func (ContractApproval) Apply2(contractApprovalInfo *ContractApproval, contractApprovalRecordList []*contract_approval_record.ContractApprovalRecord) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+	//审批单记录
+	ContractApprovalId, err := o.Insert(contractApprovalInfo)
+	if err != nil {
+		return
+	}
+	//审批单id
+	contractApprovalInfo.ContractApprovalId = int(ContractApprovalId)
+
+	for _, contractApprovalRecord := range contractApprovalRecordList {
+		//审批流记录
+		contractApprovalRecord.ContractApprovalId = contractApprovalInfo.ContractApprovalId
+		contractApprovalRecordId, tmpErr := o.Insert(contractApprovalRecord)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		//审批流id
+		contractApprovalRecord.ContractApprovalRecordId = int(contractApprovalRecordId)
+	}
+
+	//修改合同状态为待审批
+	sql := `UPDATE contract SET status="待审批",modify_time=NOW() WHERE contract_id = ?`
+	_, err = o.Raw(sql, contractApprovalInfo.ContractId).Exec()
+	return
+}
+
+//撤回审核单
+func (ContractApproval) Cancel(contractApprovalInfo *ContractApproval, contractApprovalRecord *contract_approval_record.ContractApprovalRecord) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+	//变更审批单记录
+	contractApprovalInfo.Status = "已撤回"
+	contractApprovalInfo.ModifyTime = time.Now()
+	_, err = o.Update(contractApprovalInfo)
+	if err != nil {
+		return
+	}
+
+	//变更审批流记录
+	contractApprovalRecord.Status = "已撤回"
+	contractApprovalRecord.ModifyTime = time.Now()
+	_, err = o.Update(contractApprovalRecord)
+	if err != nil {
+		return
+	}
+
+	//修改合同状态为已撤回
+	sql := `UPDATE contract SET status="已撤回",modify_time=NOW() WHERE contract_id = ?`
+	_, err = o.Raw(sql, contractApprovalInfo.ContractId).Exec()
+	return
+}
+
+func (ContractApproval) Cancel2(contractApprovalInfo *ContractApproval, contractApprovalRecordList []*contract_approval_record.ContractApprovalRecord) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+	//变更审批单记录
+	contractApprovalInfo.Status = "已撤回"
+	contractApprovalInfo.ModifyTime = time.Now()
+	_, err = o.Update(contractApprovalInfo)
+	if err != nil {
+		return
+	}
+
+	//变更所有审批流记录
+	for _, contractApprovalRecord := range contractApprovalRecordList {
+		contractApprovalRecord.Status = "已撤回"
+		contractApprovalRecord.ModifyTime = time.Now()
+		_, tmpErr := o.Update(contractApprovalRecord)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	//修改合同状态为已撤回
+	sql := `UPDATE contract SET status="已撤回",modify_time=NOW() WHERE contract_id = ?`
+	_, err = o.Raw(sql, contractApprovalInfo.ContractId).Exec()
+	return
+}
+
+//审核驳回
+func (ContractApproval) Reject(contractApprovalInfo *ContractApproval, contractApprovalRecord *contract_approval_record.ContractApprovalRecord, approveUserId int, approveUserName, remark string) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+	//变更审批单记录
+	contractApprovalInfo.Status = "已驳回"
+	contractApprovalInfo.ApproveRemark = remark //审核失败理由
+	contractApprovalInfo.ModifyTime = time.Now()
+	contractApprovalInfo.CurrNodeId = 0 //当前节点变更为0,代表当前审批流程已经结束了
+	_, err = o.Update(contractApprovalInfo)
+	if err != nil {
+		return
+	}
+
+	//变更审批流记录
+	contractApprovalRecord.Status = "已驳回"
+	contractApprovalRecord.ApproveRemark = remark            //审核失败理由
+	contractApprovalRecord.ApproveUserId = approveUserId     //审批人id
+	contractApprovalRecord.ApproveUserName = approveUserName //审批人名称
+	contractApprovalRecord.ApproveTime = time.Now()          //审批人名称
+	contractApprovalRecord.ModifyTime = time.Now()
+	_, err = o.Update(contractApprovalRecord)
+	if err != nil {
+		return
+	}
+
+	//修改合同状态为已驳回
+	sql := `UPDATE contract SET status="已驳回",modify_content="",approval_remark=?,modify_time=NOW() WHERE contract_id = ?`
+	_, err = o.Raw(sql, remark, contractApprovalInfo.ContractId).Exec()
+	return
+}
+
+//下级审批人信息
+type NextApproval struct {
+	ApproveRoleTypeCode string
+	ApproveUserId       int
+	ApproveUserName     string
+}
+
+//审核通过
+func (ContractApproval) ApprovedOld(contractApprovalInfo *ContractApproval, contractApprovalRecord *contract_approval_record.ContractApprovalRecord, approveUserId int, approveUserName, remark string, nextApproval NextApproval) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	//变更审批流记录
+	contractApprovalRecord.Status = "已审批"
+	contractApprovalRecord.ApproveRemark = remark            //审核失败理由
+	contractApprovalRecord.ApproveUserId = approveUserId     //审批人id
+	contractApprovalRecord.ApproveUserName = approveUserName //审批人名称
+	contractApprovalRecord.ApproveTime = time.Now()          //审批人名称
+	contractApprovalRecord.ModifyTime = time.Now()
+	_, err = o.Update(contractApprovalRecord)
+	if err != nil {
+		return
+	}
+
+	//判断是否存在下一级的审批人,如果不存在,那么就去变更审批单和合同的状态
+	if nextApproval.ApproveRoleTypeCode == "" && nextApproval.ApproveUserId == 0 {
+		//变更审批单记录
+		contractApprovalInfo.Status = "已审批"
+		contractApprovalInfo.ApproveRemark = remark //审核理由
+		contractApprovalInfo.ModifyTime = time.Now()
+		_, tmpErr := o.Update(contractApprovalInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		//修改合同状态为已审批
+		sql := `UPDATE contract SET status="已审批",approval_remark=?,modify_time=NOW() WHERE contract_id = ?`
+		_, tmpErr = o.Raw(sql, remark, contractApprovalInfo.ContractId).Exec()
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	} else {
+		//如果还存在下一级审批人,那么进入下级审批流
+		nextContractApprovalRecord := &contract_approval_record.ContractApprovalRecord{
+			ContractApprovalId:  contractApprovalInfo.ContractApprovalId,
+			Status:              "待审批",
+			ApproveRoleTypeCode: nextApproval.ApproveRoleTypeCode,
+			ApproveUserId:       nextApproval.ApproveUserId,
+			ApproveUserName:     nextApproval.ApproveUserName,
+			ModifyTime:          time.Now(),
+			CreateTime:          time.Now(),
+		}
+		nextContractApprovalRecordId, tmpErr := o.Insert(nextContractApprovalRecord)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		nextContractApprovalRecord.ContractApprovalRecordId = int(nextContractApprovalRecordId)
+	}
+
+	return
+}
+func (ContractApproval) Approved(contractApprovalInfo *ContractApproval, contractApprovalRecord *contract_approval_record.ContractApprovalRecord, remark string) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	//变更审批流记录
+	contractApprovalRecord.Status = "已审批"
+	contractApprovalRecord.ApproveRemark = remark   //审核失败理由
+	contractApprovalRecord.ApproveTime = time.Now() //审批时间
+	contractApprovalRecord.ModifyTime = time.Now()
+	_, err = o.Update(contractApprovalRecord, "Status", "ApproveRemark", "ApproveTime", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	//如果没有下级节点了,那么完成该审批单
+	if contractApprovalRecord.NextNodeId <= 0 {
+		//变更审批单记录
+		contractApprovalInfo.Status = "已审批"
+		contractApprovalInfo.ApproveRemark = remark                         //审核理由
+		contractApprovalInfo.CurrNodeId = contractApprovalRecord.NextNodeId //下级审批节点id
+		contractApprovalInfo.ModifyTime = time.Now()
+		_, tmpErr := o.Update(contractApprovalInfo, "Status", "ApproveRemark", "CurrNodeId", "ModifyTime")
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		//修改合同状态为已审批
+		sql := `UPDATE contract SET status="已审批",approval_remark=?,modify_time=NOW() WHERE contract_id = ?`
+		_, tmpErr = o.Raw(sql, remark, contractApprovalInfo.ContractId).Exec()
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	} else {
+		//变更审批单记录
+		contractApprovalInfo.ApproveRemark = remark                         //审核理由
+		contractApprovalInfo.CurrNodeId = contractApprovalRecord.NextNodeId //下级审批节点id
+		contractApprovalInfo.ModifyTime = time.Now()
+		_, tmpErr := o.Update(contractApprovalInfo, "ApproveRemark", "CurrNodeId", "ModifyTime")
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}
+
+//抄送
+func (ContractApproval) ApprovedByCc(contractApprovalInfo *ContractApproval, contractApprovalRecordList []*contract_approval_record.ContractApprovalRecord, remark string, nextNodeId int) (err error) {
+	o := orm.NewOrm()
+	o.Begin()
+	defer func() {
+		if err != nil {
+			o.Rollback()
+		} else {
+			o.Commit()
+		}
+	}()
+
+	for _, contractApprovalRecord := range contractApprovalRecordList {
+		//变更审批流记录
+		contractApprovalRecord.Status = "已审批"
+		contractApprovalRecord.ApproveTime = time.Now() //审批时间
+		contractApprovalRecord.ModifyTime = time.Now()
+		_, err = o.Update(contractApprovalRecord, "Status", "ApproveTime", "ModifyTime")
+		if err != nil {
+			return
+		}
+
+	}
+	if err != nil {
+		return
+	}
+
+	//如果没有下级节点了,那么完成该审批单
+	if nextNodeId <= 0 {
+		//变更审批单记录
+		contractApprovalInfo.Status = "已审批"
+		contractApprovalInfo.CurrNodeId = nextNodeId //下级审批节点id
+		contractApprovalInfo.ModifyTime = time.Now()
+		_, tmpErr := o.Update(contractApprovalInfo, "Status", "CurrNodeId", "ModifyTime")
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		//修改合同状态为已审批
+		sql := `UPDATE contract SET status="已审批",approval_remark=?,modify_time=NOW() WHERE contract_id = ?`
+		_, tmpErr = o.Raw(sql, remark, contractApprovalInfo.ContractId).Exec()
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	} else {
+		//变更审批单记录
+		contractApprovalInfo.CurrNodeId = nextNodeId //下级审批节点id
+		contractApprovalInfo.ModifyTime = time.Now()
+		_, tmpErr := o.Update(contractApprovalInfo, "CurrNodeId", "ModifyTime")
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}

+ 110 - 0
models/tables/contract_approval_record/contract_approval_record.go

@@ -0,0 +1,110 @@
+package contract_approval_record
+
+import (
+	"rdluck_tools/orm"
+	"time"
+)
+
+//合同审批单
+type ContractApprovalRecord struct {
+	ContractApprovalRecordId int       `orm:"column(contract_approval_record_id);pk"`
+	ContractApprovalId       int       `orm:"column(contract_approval_id)";description:"审批单id"`
+	Status                   string    `description:"合同状态,枚举值:待审批','已审批','已驳回','已撤回',默认待审批"`
+	ApproveRemark            string    `description:"审批人备注,可以是驳回理由,长度128位"`
+	ApproveRoleTypeCode      string    `description:"审批人角色类型,长度36位"`
+	ApproveUserId            int       `description:"审批人id"`
+	ApproveUserName          string    `description:"审批人名称"`
+	NodeId                   int       `description:"当前审批节点id"`
+	NodeType                 string    `description:"节点类型,审批人:check;抄送人:cc(Carbon Copy)"`
+	PrevNodeId               int       `description:"上级节点id"`
+	NextNodeId               int       `description:"下级节点id"`
+	ApproveTime              time.Time `description:"审批时间"`
+	ModifyTime               time.Time `description:"发起申请时间"`
+	CreateTime               time.Time `description:"最近一次审批单修改时间"`
+}
+
+//根据合同审批记录id获取合同流程记录信息
+func GetContractApprovalRecordById(contractApprovalRecordId int) (contractApprovalRecordInfo *ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_approval_record where contract_approval_record_id = ? `
+	err = o.Raw(sql, contractApprovalRecordId).QueryRow(&contractApprovalRecordInfo)
+	return
+}
+
+//根据审批单id获取当前待审批的合同审批流信息
+func GetCurrContractApprovalRecordByContractId(contractApprovalId int) (contractApprovalRecordInfo *ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select a.* from contract_approval_record a 
+ left join contract_approval b on a.contract_approval_id=b.contract_approval_id and a.node_id=b.curr_node_id
+ where a.status = "待审批" 
+  AND a.contract_approval_id=? order by a.node_id asc`
+	err = o.Raw(sql, contractApprovalId).QueryRow(&contractApprovalRecordInfo)
+	return
+}
+
+//根据审批单id和节点id获取当前待审批的合同审批流信息列表
+func GetPendingContractApprovalRecordListByContractId(contractApprovalId, nodeId int) (contractApprovalRecordList []*ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select a.* from contract_approval_record a 
+ left join contract_approval b on a.contract_approval_id=b.contract_approval_id 
+ where a.status = "待审批" 
+  AND a.contract_approval_id=? AND node_id  >= ? order by a.node_id asc`
+	_, err = o.Raw(sql, contractApprovalId, nodeId).QueryRows(&contractApprovalRecordList)
+	return
+}
+
+//根据审批单id和节点id获取当前节点的合同审批流信息列表
+func GetContractApprovalRecordListByContractIdAndNode(contractApprovalId, nodeId int) (contractApprovalRecordList []*ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select a.* from contract_approval_record a 
+ join contract_approval b on a.contract_approval_id=b.contract_approval_id
+ where a.status = "待审批" 
+  AND a.contract_approval_id=? AND node_id  = ? order by a.node_id asc`
+	_, err = o.Raw(sql, contractApprovalId, nodeId).QueryRows(&contractApprovalRecordList)
+	return
+}
+
+//根据审批单id和节点id获取当前节点的合同审批流信息列表
+func GetContractApprovalRecordListByContractApprovalId(contractApprovalId int) (contractApprovalRecordList []*ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select a.* from contract_approval_record a 
+  join contract_approval b on a.contract_approval_id=b.contract_approval_id
+  AND a.contract_approval_id=? AND node_type  = "cc" order by a.node_id asc`
+	_, err = o.Raw(sql, contractApprovalId).QueryRows(&contractApprovalRecordList)
+	return
+}
+
+//根据审批单id获取最近一次待审批的合同审批流信息
+func GetLastPendingContractApprovalRecordByContractId(contractApprovalId int) (contractApprovalRecordInfo *ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_approval_record where status = "待审批" 
+  AND contract_approval_id=? order by contract_approval_record_id desc`
+	err = o.Raw(sql, contractApprovalId).QueryRow(&contractApprovalRecordInfo)
+	return
+}
+
+//根据审批单id批量获取最新的审批流列表数据
+func GetContractApprovalRecordListOld(contractApprovalIds string) (list []*ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `
+SELECT
+a.* from contract_approval_record a
+	join 
+	( SELECT max( contract_approval_record_id ) max_id,contract_approval_id FROM contract_approval_record where contract_approval_id in (` + contractApprovalIds + `) GROUP BY contract_approval_id `
+	sql += ` ) b on a.contract_approval_record_id=b.max_id
+WHERE a.contract_approval_id in (` + contractApprovalIds + `) `
+	sql += ` order by contract_approval_record_id desc  `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+func GetContractApprovalRecordList(contractApprovalIds string, approveUserId int) (list []*ContractApprovalRecord, err error) {
+	o := orm.NewOrm()
+	sql := `
+SELECT
+a.* from contract_approval_record a
+	join contract_approval b on a.contract_approval_id = b.contract_approval_id and a.node_id=b.curr_node_id
+WHERE a.contract_approval_id in (` + contractApprovalIds + `) AND (approve_user_id = ? or approve_user_id=0)`
+	sql += ` order by contract_approval_record_id desc  `
+	_, err = o.Raw(sql, approveUserId).QueryRows(&list)
+	return
+}

+ 42 - 0
models/tables/contract_operation_record/contract_operation_record.go

@@ -0,0 +1,42 @@
+package contract_operation_record
+
+import (
+	"rdluck_tools/orm"
+	"time"
+)
+
+//合同操作记录单
+type ContractOperationRecord struct {
+	Id               int       `orm:"column(id);pk"`
+	ContractId       int       `orm:"column(contract_id)";description:"合同id"`
+	Operation        string    `description:"操作类型,add:新增,edit:编辑,apply:发起审批,cancel_apply:撤回审批,reject:拒绝审批,approval:审批操作,invalid:作废;长度36位"`
+	OpUserId         int       `description:"操作人id"`
+	OpUserName       string    `description:"操作人名称"`
+	Remark           string    `description:"备注,长度255位"`
+	ApprovalRecordId int       `orm:"column(approval_record_id)";description:"审批流id"`
+	CreateTime       time.Time `description:"日志添加时间"`
+}
+
+//根据合同id获取整个合同的操作日志
+func GetContractOperationRListByContractId(contractId int) (list []*ContractOperationRecord, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_operation_record where contract_id = ? order by id desc `
+	_, err = o.Raw(sql, contractId).QueryRows(&list)
+	return
+}
+
+//添加合同操作日志
+func AddContractOperationRecord(contractId, opUserId, approvalRecordId int, operation, opUserName, remark string) (err error) {
+	o := orm.NewOrm()
+	contractOperationRecord := &ContractOperationRecord{
+		ContractId:       contractId,
+		Operation:        operation,
+		OpUserId:         opUserId,
+		OpUserName:       opUserName,
+		Remark:           remark,
+		ApprovalRecordId: approvalRecordId,
+		CreateTime:       time.Now(),
+	}
+	_, err = o.Insert(contractOperationRecord)
+	return
+}

+ 38 - 0
models/tables/contract_service_detail/contract_service_detail.go

@@ -0,0 +1,38 @@
+package contract_service_detail
+
+import (
+	"rdluck_tools/orm"
+	"time"
+)
+
+//合同的服务内容详情,表单数据
+type ContractServiceDetail struct {
+	Id                int       `orm:"column(id);pk"`
+	ContractServiceId int       `description:"合同服务id"`
+	ContractId        int       `description:"合同id"`
+	ServiceTemplateId int       `description:"服务模板id"`
+	Col1              string    `orm:"column(col_1)"description:"第1列数据"`
+	Col2              string    `orm:"column(col_2)"description:"第2列数据"`
+	Col3              string    `orm:"column(col_3)"description:"第3列数据"`
+	Col4              string    `orm:"column(col_4)"description:"第4列数据"`
+	Col5              string    `orm:"column(col_5)"description:"第5列数据"`
+	Col6              string    `orm:"column(col_6)"description:"第6列数据"`
+	Col7              string    `orm:"column(col_7)"description:"第7列数据"`
+	CreateTime        time.Time `description:"数据添加时间"`
+}
+
+//根据服务模板id获取对应的套餐表格数据详情
+func GetContractServiceDetailByTemplateId(serviceTemplateId int) (list []*ContractServiceDetail, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_service_detail where service_template_id = ? and contract_service_id = 0 order by id asc`
+	_, err = o.Raw(sql, serviceTemplateId).QueryRows(&list)
+	return
+}
+
+//根据合同服务id获取对应的套餐表格数据详情
+func GetContractServiceDetailListByServiceId(contractServiceId int) (list []*ContractServiceDetail, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_service_detail where  contract_service_id = ? order by id asc`
+	_, err = o.Raw(sql, contractServiceId).QueryRows(&list)
+	return
+}

+ 68 - 0
models/tables/contract_service_template/contract_service_template.go

@@ -0,0 +1,68 @@
+package contract_service_template
+
+import (
+	"hongze/hongze_mobile_admin/models/tables/contract_service_detail"
+	"rdluck_tools/orm"
+	"time"
+)
+
+type ContractServiceTemplate struct {
+	ServiceTemplateId int       `orm:"column(service_template_id);pk"`
+	ProductId         string    `description:"产品id"`
+	Pid               int       `description:"父级id"`
+	Title             string    `description:"套餐名称"`
+	Value             string    `description:"对应的数据"`
+	SelectType        string    `description:"选择器类型,单选还是多选,radio;单选:checkbox"`
+	SelectName        string    `description:"选择器名称"`
+	TableValue        string    `description:"表格数据,用于word生成时的json数据"`
+	Remark            string    `description:"表头备注"`
+	CreateTime        time.Time `description:"创建时间"`
+	ModifyTime        time.Time `description:"修改时间"`
+}
+
+type ContractServiceTemplateMapItems struct {
+	ServiceTemplateId int    `orm:"column(service_template_id);pk"`
+	ProductId         string `description:"产品id"`
+	Pid               int    `description:"父级id"`
+	Title             string `description:"套餐名称"`
+	Value             string `description:"对应的数据"`
+	SelectType        string `description:"选择器类型,单选还是多选,radio;单选:checkbox"`
+	SelectName        string `description:"选择器名称"`
+	Children          []*ContractServiceTemplateMapItems
+	Detail            []*contract_service_detail.ContractServiceDetail
+}
+
+//根据服务模板id获取合同模板详情
+func GetContractServiceTemplateById(contractServiceTemplateId int) (contractServiceTemplate *ContractServiceTemplate, err error) {
+	o := orm.NewOrm()
+	sql := `select * from contract_service_template where service_template_id = ? `
+	err = o.Raw(sql, contractServiceTemplateId).QueryRow(&contractServiceTemplate)
+	return
+}
+
+type ContractServiceTemplateMapResp struct {
+	FiccTemplate []*ContractServiceTemplateMapItems
+	RiaTemplate  []*ContractServiceTemplateMapItems
+}
+
+func GetContractServiceTemplateMapByProductId(productId int) (items []*ContractServiceTemplateMapItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM contract_service_template WHERE product_id=? and pid=0`
+	_, err = o.Raw(sql, productId).QueryRows(&items)
+	return
+}
+
+func GetContractServiceTemplateMapByParentId(parentId int) (items []*ContractServiceTemplateMapItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM contract_service_template WHERE pid=?`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+//获取所有的合同套餐列表
+func GetAllContractServiceTemplateList() (items []*ContractServiceTemplate, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM contract_service_template`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 38 - 0
models/tables/contract_template/contract_template.go

@@ -0,0 +1,38 @@
+package contract_template
+
+import (
+	"rdluck_tools/orm"
+	"time"
+)
+
+type ContractTemplate struct {
+	ContractTemplateId int       `orm:"column(contract_template_id);pk"`
+	ProductId          int       `description:"产品id"`
+	Title              string    `description:"标题"`
+	Url                string    `description:"模板地址"`
+	Html               string    `description:"html模板"`
+	PdfHtml            string    `description:"生成pdf的html模板"`
+	CreateTime         time.Time `description:"创建时间"`
+}
+
+func GetContractTemplateMapByProductId(productId int) (items []*ContractTemplate, err error) {
+	var condition string
+	pars := make([]interface{}, 0)
+	if productId > 0 {
+		condition += `AND product_id = ? `
+		pars = append(pars, productId)
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM contract_template WHERE 1=1 `
+	sql += condition
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+//根据模板id获取模板信息
+func GetContractTemplateByTemplateId(templateId int) (item *ContractTemplate, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM contract_template WHERE contract_template_id=? `
+	err = o.Raw(sql, templateId).QueryRow(&item)
+	return
+}

+ 14 - 2
routers/router.go

@@ -45,8 +45,20 @@ func init() {
 				&controllers.MessageCommon{},
 			),
 		),
-		beego.NSInclude(
-			&controllers.ContractCommon{},
+		beego.NSNamespace("/company_contract",
+			beego.NSInclude(
+				&controllers.CompanyContractCommon{},
+			),
+		),
+		beego.NSNamespace("/contract",
+			beego.NSInclude(
+				&controllers.ContractCommon{},
+			),
+		),
+		beego.NSNamespace("/contract_approval",
+			beego.NSInclude(
+				&controllers.ContractApprovalCommon{},
+			),
 		),
 	)
 	beego.AddNamespace(ns)

+ 4 - 2
services/company_approval_message.go

@@ -6,7 +6,7 @@ import (
 	"time"
 )
 
-func AddCompanyApprovalMessage(createUserId, receiveUserId, companyId, companyApprovalId, msgType int, companyName, remark, content, mobile string) (err error) {
+func AddCompanyApprovalMessage(createUserId, receiveUserId, companyId, companyApprovalId, msgType, sourceType, approvalStatus int, companyName, remark, content, mobile string) (err error) {
 	var err1 error
 	defer func() {
 		if err != nil {
@@ -17,7 +17,7 @@ func AddCompanyApprovalMessage(createUserId, receiveUserId, companyId, companyAp
 		}
 	}()
 	if msgType == 2 {
-		company_approval_message.ModifyCompanyApprovalMessage(companyApprovalId)
+		company_approval_message.ModifyCompanyApprovalMessage(companyApprovalId, sourceType)
 	}
 	msgItem := new(company_approval_message.CompanyApprovalMessage)
 	msgItem.CreateUserId = createUserId
@@ -32,6 +32,8 @@ func AddCompanyApprovalMessage(createUserId, receiveUserId, companyId, companyAp
 	msgItem.CompanyApprovalId = companyApprovalId
 	msgItem.OperationStatus = 1
 	msgItem.MessageType = msgType
+	msgItem.SourceType = sourceType         //消息来源
+	msgItem.ApprovalStatus = approvalStatus //审批状态,1:待审批,2:已审批,3:已驳回
 	err = company_approval_message.AddCompanyApprovalMessage(msgItem)
 	if mobile != "" {
 		go SendCompanyApplyWxTemplateMsg(mobile, content)

+ 38 - 0
services/contract/contract.go

@@ -0,0 +1,38 @@
+package contract
+
+import (
+	"errors"
+	"fmt"
+	contractCustom "hongze/hongze_mobile_admin/models/custom/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract_service_detail"
+	"hongze/hongze_mobile_admin/utils"
+)
+
+//根据id获取合同详情(包含服务)
+func GetContractDetail(contractId int) (contractDetail *contract.ContractDetail, err error) {
+	contractDetail, err = contract.GetContractDetailById(contractId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = errors.New("找不到该合同")
+		}
+		return
+	}
+	serviceList, err := contractCustom.GetContractServiceAndDetailList(contractId)
+	if err != nil {
+		err = errors.New(fmt.Sprint("查找合同服务异常", err))
+		return
+	}
+	for i := 0; len(serviceList) > i; i++ {
+		if serviceList[i].HasDetail == "是" {
+			list, detailErr := contract_service_detail.GetContractServiceDetailListByServiceId(serviceList[i].ContractServiceId)
+			if detailErr != nil {
+				err = errors.New(fmt.Sprint("查找合同服务详情异常", detailErr))
+				return
+			}
+			serviceList[i].DetailList = list
+		}
+	}
+	contractDetail.Service = serviceList
+	return
+}

+ 664 - 0
services/contract/contract_approval.go

@@ -0,0 +1,664 @@
+package contract
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"hongze/hongze_mobile_admin/models/custom"
+	"hongze/hongze_mobile_admin/models/tables/admin"
+	"hongze/hongze_mobile_admin/models/tables/approval_flow"
+	"hongze/hongze_mobile_admin/models/tables/approval_flow_node"
+	"hongze/hongze_mobile_admin/models/tables/company"
+	"hongze/hongze_mobile_admin/models/tables/company_approval_message"
+	"hongze/hongze_mobile_admin/models/tables/company_log"
+	"hongze/hongze_mobile_admin/models/tables/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract_approval"
+	"hongze/hongze_mobile_admin/models/tables/contract_approval_record"
+	"hongze/hongze_mobile_admin/models/tables/contract_operation_record"
+	"hongze/hongze_mobile_admin/services"
+	"hongze/hongze_mobile_admin/services/flow"
+	"hongze/hongze_mobile_admin/utils"
+	"os"
+	"strings"
+	"time"
+)
+
+//提交审批
+func Apply(contractId int) (err error) {
+	//获取合同详情
+	contractInfo, err := GetContractDetail(contractId)
+
+	if err != nil {
+		err = errors.New("获取合同信息失败,Err:" + err.Error())
+		return
+	}
+	if contractInfo == nil {
+		err = errors.New("合同异常,没有合同信息")
+		return
+	}
+	contractInfo.StartDateStr = contractInfo.StartDate.Format(utils.FormatDate)
+	contractInfo.EndDateStr = contractInfo.EndDate.Format(utils.FormatDate)
+	contractInfo.ModifyTimeStr = contractInfo.ModifyTime.Format(utils.FormatDateTime)
+	contractInfo.CreateTimeStr = contractInfo.CreateTime.Format(utils.FormatDateTime)
+
+	//合同状态判断
+	ignoreStatus := []string{"待提交", "已撤回", "已驳回"}
+	if !strings.Contains(strings.Join(ignoreStatus, ","), contractInfo.Status) {
+		err = errors.New("合同状态异常,不允许提交审批,当前合同状态:" + contractInfo.Status)
+		return
+	}
+
+	//待审内容
+	applyContent := "生成新模板"
+	//if contractInfo.SourceId > 0 {
+	//	applyContent = "作废重生成"
+	//}
+	if contractInfo.Status == "已驳回" {
+		applyContent = "驳回重申请"
+	}
+	//if contractInfo.Status == "已撤回" {
+	//	applyContent = "撤回重申请"
+	//}
+
+	contractDetailByte, err := json.Marshal(contractInfo)
+	if err != nil {
+		err = errors.New(fmt.Sprint("合同状态异常,当前合同数据格式化失败:", err))
+		return
+	}
+
+	//获取基础审批流
+	var flowItemInfo *approval_flow.ApprovalFlowItem
+	if contractInfo.ProductId == 1 {
+		tmpFlowItemInfo, tmpErr := flow.GetApprovalFlow(3)
+		if tmpErr != nil {
+			err = errors.New(fmt.Sprint("查找审批流程配置失败:", tmpErr))
+			return
+		}
+		flowItemInfo = tmpFlowItemInfo
+	} else {
+		tmpFlowItemInfo, tmpErr := flow.GetApprovalFlow(4)
+		if tmpErr != nil {
+			err = errors.New(fmt.Sprint("查找审批流程配置失败:", tmpErr))
+			return
+		}
+		flowItemInfo = tmpFlowItemInfo
+	}
+	fmt.Println(flowItemInfo)
+
+	//校验是否存在待审批的审批单(其实没有多大意义,只是为了 异常数据校验)
+	contractApproval := &contract_approval.ContractApproval{
+		ContractId:     contractInfo.ContractId,
+		Status:         "待审批",
+		ApplyContent:   applyContent,
+		ContractDetail: string(contractDetailByte),
+		ApplyUserId:    contractInfo.SellerId,
+		ApplyUserName:  contractInfo.SellerName,
+		FlowId:         flowItemInfo.FlowId,
+		FlowVersion:    flowItemInfo.CurrVersion,
+		CurrNodeId:     flowItemInfo.NodeList[0].NodeId,
+		ModifyTime:     time.Now(),
+		CreateTime:     time.Now(),
+	}
+	has, err := contractApproval.CheckPendingByContractId(contractInfo.ContractId)
+	if err != nil {
+		return
+	}
+	if has {
+		err = errors.New("合同异常,不允许提交审批,存在待审核的审批单")
+		return
+	}
+	contractApprovalRecordList := make([]*contract_approval_record.ContractApprovalRecord, 0)
+
+	ApproveUserMap := make(map[int]approval_flow_node.User)
+	for _, nodeItem := range flowItemInfo.NodeList {
+		//审批流记录
+		if len(nodeItem.UserList) <= 0 {
+			err = errors.New(fmt.Sprint("审批流程异常,没有可审批的人员,Err:", err))
+			return
+		}
+		for _, userItem := range nodeItem.UserList {
+			contractApprovalRecord := &contract_approval_record.ContractApprovalRecord{
+				Status:              "待审批",
+				ApproveRemark:       "",
+				ApproveUserId:       userItem.AdminId,
+				ApproveUserName:     userItem.Name,
+				ApproveRoleTypeCode: userItem.RoleTypeCode,
+				NodeId:              nodeItem.NodeId,
+				NodeType:            nodeItem.NodeType,
+				NextNodeId:          nodeItem.NextNodeId,
+				CreateTime:          time.Now(),
+				ModifyTime:          time.Now(),
+			}
+			contractApprovalRecordList = append(contractApprovalRecordList, contractApprovalRecord)
+			ApproveUserMap[userItem.AdminId] = userItem
+		}
+
+	}
+
+	err = contractApproval.Apply2(contractApproval, contractApprovalRecordList)
+	if err != nil {
+		return
+	}
+
+	//添加操作日志
+	remark := "提交审批"
+	_ = contract_operation_record.AddContractOperationRecord(contractInfo.ContractId, contractInfo.SellerId, contractApprovalRecordList[0].ContractApprovalRecordId, "apply", contractInfo.SellerName, remark)
+
+	//待办通知
+	{
+		//发送消息下级审批人
+		go messageToNodeUser(contractApprovalRecordList[0].NodeId, contractInfo.SellerId, contractApproval.ContractApprovalId, 1, 1, contractInfo.CompanyName, applyContent)
+
+		//发送消息给所有的抄送人
+		go messageToAllCcUser(contractInfo.SellerId, contractApproval.ContractApprovalId, 1, 1, contractInfo.CompanyName, applyContent)
+	}
+	return
+}
+
+//撤销合同
+func CancelApplyByContract(contractInfo *contract.Contract, opUserId int) (err error) {
+	if contractInfo == nil {
+		err = errors.New("审批流异常,没有审批流信息")
+		return
+	}
+
+	//合同状态判断
+	if contractInfo.Status != "待审批" {
+		err = errors.New("合同状态异常,不允许撤回申请,当前合同状态:" + contractInfo.Status)
+		return
+	}
+
+	//校验操作人与合同申请人是否同一人
+	if contractInfo.SellerId != opUserId {
+		err = errors.New("操作人异常,不允许撤回非本人提交的申请:" + contractInfo.Status)
+		return
+	}
+
+	//获取最近一次待审批的审批单信息
+	contractApproval, err := contract_approval.GetLastPendingContractApprovalByContractId(contractInfo.ContractId)
+	if err != nil {
+		return
+	}
+	//获取当前待审批的审批流信息
+	contractApprovalRecord, err := contract_approval_record.GetCurrContractApprovalRecordByContractId(contractApproval.ContractApprovalId)
+	if err != nil {
+		return
+	}
+	//判断当前节点是否存在上级节点,如果存在,那么说明
+	if contractApprovalRecord.PrevNodeId > 0 {
+		err = errors.New("合同已存在审批操作,不允许撤回申请")
+		return
+	}
+
+	//获取当前审批单中所有待审批的流程流
+	contractApprovalRecordList, err := contract_approval_record.GetPendingContractApprovalRecordListByContractId(contractApproval.ContractApprovalId, contractApprovalRecord.NodeId)
+	if err != nil {
+		return
+	}
+
+	//撤回审批流
+	err = contractApproval.Cancel2(contractApproval, contractApprovalRecordList)
+	if err != nil {
+		return
+	}
+
+	//添加操作日志
+	remark := "撤回申请"
+	_ = contract_operation_record.AddContractOperationRecord(contractInfo.ContractId, contractInfo.SellerId, contractApprovalRecord.ContractApprovalRecordId, "cancel_apply", contractInfo.SellerName, remark)
+
+	//作废原有消息
+	for _, contractApprovalRecord := range contractApprovalRecordList {
+		go company_approval_message.CancelCompanyApprovalMessage(contractApprovalRecord.ContractApprovalRecordId, 2)
+	}
+
+	return
+}
+
+//撤回审批单
+func CancelApply(contractApprovalRecord *contract_approval_record.ContractApprovalRecord, opUserId int) (err error) {
+	if contractApprovalRecord == nil {
+		err = errors.New("审批流异常,没有审批流信息")
+		return
+	}
+
+	//审批流状态判断
+	if contractApprovalRecord.Status != "待审批" {
+		err = errors.New("审批流状态异常,不允许撤回申请,当前审批流状态:" + contractApprovalRecord.Status)
+		return
+	}
+
+	//获取审批单详情
+	contractApproval, err := contract_approval.GetContractApprovalById(contractApprovalRecord.ContractApprovalId)
+	if err != nil {
+		return
+	}
+
+	//审批单状态判断
+	if contractApproval.Status != "待审批" {
+		err = errors.New("审批单状态异常,不允许撤回申请,当前审批单状态:" + contractApproval.Status)
+		return
+	}
+
+	//获取合同信息
+	contractInfo, err := contract.GetContractById(contractApproval.ContractId)
+	if err != nil {
+		return
+	}
+	//合同状态判断
+	if contractInfo.Status != "待审批" {
+		err = errors.New("合同状态异常,不允许撤回申请,当前合同状态:" + contractInfo.Status)
+		return
+	}
+
+	//校验操作人与合同申请人是否同一人
+	if contractInfo.SellerId != opUserId {
+		err = errors.New("操作人异常,不允许撤回非本人提交的申请:" + contractInfo.Status)
+		return
+	}
+	err = contractApproval.Cancel(contractApproval, contractApprovalRecord)
+	if err != nil {
+		return
+	}
+
+	//添加操作日志
+	remark := "撤回申请"
+	_ = contract_operation_record.AddContractOperationRecord(contractInfo.ContractId, contractInfo.SellerId, contractApprovalRecord.ContractApprovalRecordId, "cancel_apply", contractInfo.SellerName, remark)
+
+	//作废原有消息
+	go company_approval_message.CancelCompanyApprovalMessage(contractApprovalRecord.ContractApprovalRecordId, 2)
+
+	return
+}
+
+//驳回审批
+func Reject(contractApprovalRecord *contract_approval_record.ContractApprovalRecord, opUser *custom.AdminWx, rejectRemark string) (err error) {
+	if contractApprovalRecord == nil {
+		err = errors.New("审批流异常,没有审批流信息")
+		return
+	}
+
+	//审批流状态判断
+	if contractApprovalRecord.Status != "待审批" {
+		err = errors.New("审批流状态异常,不允许驳回申请,当前审批流状态:" + contractApprovalRecord.Status)
+		return
+	}
+
+	//判断是否审批类型,如果不是审批类型,那么就没有审批权限
+	if contractApprovalRecord.NodeType != "check" {
+		err = errors.New("当前账号没有审批权限")
+		return
+	}
+
+	//操作人
+	opUserId := opUser.AdminId
+	opUserName := opUser.RealName
+
+	//操作权限校验
+	if opUser.RoleTypeCode != contractApprovalRecord.ApproveRoleTypeCode {
+		err = errors.New("当前账号没有审批权限")
+		return
+	}
+	if contractApprovalRecord.ApproveUserId > 0 && contractApprovalRecord.ApproveUserId != opUserId {
+		err = errors.New("当前账号没有审批权限,需要指定人操作")
+		return
+	}
+
+	//获取审批单详情
+	contractApproval, err := contract_approval.GetContractApprovalById(contractApprovalRecord.ContractApprovalId)
+	if err != nil {
+		return
+	}
+
+	//审批单状态判断
+	if contractApproval.Status != "待审批" {
+		err = errors.New("审批单状态异常,不允许驳回申请,当前审批单状态:" + contractApproval.Status)
+		return
+	}
+
+	//获取合同信息
+	contractInfo, err := contract.GetContractById(contractApproval.ContractId)
+	if err != nil {
+		return
+	}
+	//合同状态判断
+	if contractInfo.Status != "待审批" {
+		err = errors.New("合同状态异常,不允许驳回申请,当前合同状态:" + contractInfo.Status)
+		return
+	}
+	err = contractApproval.Reject(contractApproval, contractApprovalRecord, opUserId, opUserName, rejectRemark)
+	if err != nil {
+		return
+	}
+
+	//添加操作日志
+	remark := "驳回申请"
+	_ = contract_operation_record.AddContractOperationRecord(contractInfo.ContractId, opUserId, contractApprovalRecord.ContractApprovalRecordId, "reject", opUserName, remark)
+
+	//待办通知
+	{
+		content := contractInfo.CompanyName + " 合同模板已驳回"
+		approvalSysUser, _ := admin.GetAdminById(contractInfo.SellerId)
+		go services.AddCompanyApprovalMessage(opUserId, contractInfo.SellerId, 0, contractApprovalRecord.ContractApprovalRecordId, 2, 2, 3, contractInfo.CompanyName, content, content, approvalSysUser.Mobile)
+
+		//发送消息给所有的抄送人
+		go messageToAllCcUser(opUserId, contractApprovalRecord.ContractApprovalId, 2, 3, contractInfo.CompanyName, content)
+	}
+	return
+}
+
+//审批通过
+func Approved(contractApprovalRecord *contract_approval_record.ContractApprovalRecord, opUser *custom.AdminWx, approvedRemark string) (err error) {
+	if contractApprovalRecord == nil {
+		err = errors.New("审批流异常,没有审批流信息")
+		return
+	}
+
+	//审批流状态判断
+	if contractApprovalRecord.Status != "待审批" {
+		err = errors.New("审批流状态异常,不允许审批,当前审批流状态:" + contractApprovalRecord.Status)
+		return
+	}
+
+	//判断是否审批类型,如果不是审批类型,那么就没有审批权限
+	if contractApprovalRecord.NodeType != "check" {
+		err = errors.New("当前账号没有审批权限")
+		return
+	}
+
+	//操作人
+	opUserId := opUser.AdminId
+	opUserName := opUser.RealName
+	//操作权限校验
+	if opUser.RoleTypeCode != contractApprovalRecord.ApproveRoleTypeCode {
+		err = errors.New("当前账号没有审批权限")
+		return
+	}
+	if contractApprovalRecord.ApproveUserId > 0 && contractApprovalRecord.ApproveUserId != opUserId {
+		err = errors.New("当前账号没有审批权限,需要指定人操作")
+		return
+	}
+
+	//获取审批单详情
+	contractApproval, err := contract_approval.GetContractApprovalById(contractApprovalRecord.ContractApprovalId)
+	if err != nil {
+		return
+	}
+
+	//审批单状态判断
+	if contractApproval.Status != "待审批" {
+		err = errors.New("审批单状态异常,不允许审批,当前审批单状态:" + contractApproval.Status)
+		return
+	}
+
+	//获取合同信息
+	contractInfo, err := contract.GetContractById(contractApproval.ContractId)
+	if err != nil {
+		return
+	}
+	//合同状态判断
+	if contractInfo.Status != "待审批" {
+		err = errors.New("合同状态异常,不允许审批,当前合同状态:" + contractInfo.Status)
+		return
+	}
+
+	//if contractApprovalRecord
+	err = contractApproval.Approved(contractApproval, contractApprovalRecord, approvedRemark)
+	if err != nil {
+		return
+	}
+
+	//添加操作日志
+	remark := "审批通过"
+	_ = contract_operation_record.AddContractOperationRecord(contractInfo.ContractId, opUserId, contractApprovalRecord.ContractApprovalRecordId, "approval", opUserName, remark)
+
+	//发送信息给所有抄送人
+	content := contractInfo.CompanyName + " 合同模板已审核"
+	go messageToAllCcUser(opUserId, contractApprovalRecord.ContractApprovalId, 2, 2, contractInfo.CompanyName, content)
+	//如果下一个节点属于结束节点,那么通知对应的销售,同时,异步生成合同pdf
+	if contractApprovalRecord.NextNodeId == 0 {
+		//待办通知(通知销售已经审核通过了)
+		{
+			content := contractInfo.CompanyName + " 合同模板已审核"
+			approvalSysUser, _ := admin.GetAdminById(contractInfo.SellerId)
+			go services.AddCompanyApprovalMessage(opUserId, contractInfo.SellerId, 0, contractApprovalRecord.ContractApprovalRecordId, 2, 2, 2, contractInfo.CompanyName, content, content, approvalSysUser.Mobile)
+		}
+
+		//审核通过后的异步操作(pdf生成、修改数据等)
+		go AfterApproved(contractInfo.ContractId)
+	} else {
+		//获取下级节点信息
+		flowNodeInfo, tmpErr := approval_flow_node.GetByNodeId(contractApprovalRecord.NextNodeId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		//如果该级节点是抄送类型,那么需要将该节点给处理掉
+		if flowNodeInfo.NodeType == "cc" {
+			go approvedByCc(approvedRemark, contractApprovalRecord)
+		} else {
+			//发送消息下级审批人
+			go messageToNodeUser(contractApprovalRecord.NextNodeId, contractInfo.SellerId, contractApproval.ContractApprovalId, 1, 1, contractInfo.CompanyName, content)
+		}
+	}
+
+	return
+}
+
+//审批通过(抄送节点)
+func approvedByCc(approvedRemark string, sourceContractApprovalRecord *contract_approval_record.ContractApprovalRecord) (err error) {
+	//下个流程节点id
+	nextNodeId := 0
+	//获取审批单中抄送节点的所有数据列表
+	contractApprovalRecordList, err := contract_approval_record.GetContractApprovalRecordListByContractIdAndNode(sourceContractApprovalRecord.ContractApprovalId, sourceContractApprovalRecord.NextNodeId)
+	if err != nil {
+		return
+	}
+	//遍历所有的抄送单
+	for _, contractApprovalRecord := range contractApprovalRecordList {
+		nextNodeId = contractApprovalRecord.NextNodeId
+		//审批流状态判断
+		if contractApprovalRecord.Status != "待审批" {
+			err = errors.New("审批流状态异常,不允许审批,当前审批流状态:" + contractApprovalRecord.Status)
+			return
+		}
+		//判断是否审批类型,如果不是审批类型,那么就没有审批权限
+		if contractApprovalRecord.NodeType != "cc" {
+			err = errors.New("当前账号不是抄送权限")
+			return
+		}
+	}
+
+	//获取审批单详情
+	contractApproval, err := contract_approval.GetContractApprovalById(sourceContractApprovalRecord.ContractApprovalId)
+	if err != nil {
+		return
+	}
+
+	//获取合同信息
+	contractInfo, err := contract.GetContractById(contractApproval.ContractId)
+	if err != nil {
+		return
+	}
+
+	//if contractApprovalRecord
+	err = contractApproval.ApprovedByCc(contractApproval, contractApprovalRecordList, approvedRemark, nextNodeId)
+	if err != nil {
+		return
+	}
+
+	//如果下一个节点属于结束节点,那么通知对应的销售,同时,异步生成合同pdf
+	if nextNodeId == 0 {
+		//待办通知
+		{
+			content := contractInfo.CompanyName + " 合同模板已审核"
+			approvalSysUser, _ := admin.GetAdminById(contractInfo.SellerId)
+			go services.AddCompanyApprovalMessage(sourceContractApprovalRecord.ApproveUserId, contractInfo.SellerId, 0, sourceContractApprovalRecord.ContractApprovalRecordId, 2, 2, 2, contractInfo.CompanyName, content, content, approvalSysUser.Mobile)
+		}
+
+		//审核通过后的异步操作(pdf生成、修改数据等)
+		go AfterApproved(contractInfo.ContractId)
+	} else {
+		//获取下级节点信息
+		flowNodeInfo, tmpErr := approval_flow_node.GetByNodeId(nextNodeId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		//如果下级节点是抄送类型,那么还是需要处理抄送节点逻辑
+		if flowNodeInfo.NodeType == "cc" {
+			go approvedByCc(approvedRemark, sourceContractApprovalRecord)
+		} else {
+			//如果下级级节点是审批类型
+			//发送消息下级审批人
+			go messageToNodeUser(nextNodeId, contractInfo.SellerId, contractApproval.ContractApprovalId, 1, 1, contractInfo.CompanyName, contractApproval.ApplyContent)
+		}
+	}
+
+	return
+}
+
+//抄送信息给用户(根据节点来推送)
+func messageToNodeUser(flowNodeId, createUserId, contractApprovalId, messageType, approvalStatus int, companyName, content string) (err error) {
+	//获取审批单中抄送节点的所有数据列表
+	contractApprovalRecordList, err := contract_approval_record.GetContractApprovalRecordListByContractIdAndNode(contractApprovalId, flowNodeId)
+	if err != nil {
+		return
+	}
+	err = messageToUser(contractApprovalRecordList, createUserId, messageType, approvalStatus, companyName, content)
+	return
+}
+
+//发送信息给所有抄送人
+// @param createUserId 消息发起人
+// @param contractApprovalId 审批单id
+// @param messageType 消息类型:1:申请消息,2:审批结果
+// @param approvalStatus 审批状态:1:待审批,2:已审批,3:已驳回"
+// @param companyName 企业客户名称
+// @param content 消息内容
+func messageToAllCcUser(createUserId, contractApprovalId, messageType, approvalStatus int, companyName, content string) (err error) {
+	//获取审批单中抄送节点的所有数据列表
+	contractApprovalRecordList, err := contract_approval_record.GetContractApprovalRecordListByContractApprovalId(contractApprovalId)
+
+	if err != nil {
+		return
+	}
+	err = messageToUser(contractApprovalRecordList, createUserId, messageType, approvalStatus, companyName, content)
+	return
+}
+
+//抄送信息给用户(发送给所有抄送人)
+// @param createUserId 待审批、抄送记录
+// @param createUserId 消息发起人
+// @param contractApprovalId 审批单id
+// @param messageType 消息类型:1:申请消息,2:审批结果
+// @param approvalStatus 审批状态:1:待审批,2:已审批,3:已驳回"
+// @param companyName 企业客户名称
+// @param content 消息内容
+func messageToUser(contractApprovalRecordList []*contract_approval_record.ContractApprovalRecord, createUserId, messageType, approvalStatus int, companyName, content string) (err error) {
+	for _, contractApprovalRecord := range contractApprovalRecordList {
+		sysUserMobile := ""
+		sysUser, _ := admin.GetAdminById(contractApprovalRecord.ApproveUserId)
+		if sysUser != nil {
+			sysUserMobile = sysUser.Mobile
+		}
+		//接收人,就是审批记录里面的待审核人(抄送人)
+		receiveUserId := contractApprovalRecord.ApproveUserId
+		go services.AddCompanyApprovalMessage(createUserId, receiveUserId, 0, contractApprovalRecord.ContractApprovalRecordId, messageType, 2, approvalStatus, companyName, content, content, sysUserMobile)
+	}
+	return
+}
+
+//审批完成后操作
+func AfterApproved(contractId int) (err error) {
+	defer func() {
+		if err != nil {
+			//fmt.Println("异常提醒:"+utils.RunMode, "合同审批通过后,生成pdf失败,ERR:"+err.Error()+"", utils.EmailSendToUsers)
+			go utils.SendEmail("异常提醒:"+utils.RunMode, "合同审批通过后,生成pdf失败,ERR:"+err.Error()+"", utils.EmailSendToUsers)
+		}
+
+	}()
+	contractDetail, err := GetContractDetail(contractId)
+	if err != nil {
+		return
+	}
+	//fmt.Println("contractDetail:", contractDetail)
+
+	//pdf生成并保存
+	{
+		//获取合同的html模板信息
+		contractHtml, tmpErr := services.GetHtmlByContractDetail(contractDetail, "pdf")
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		//生成pdf
+		pdfPath := fmt.Sprint("./static/word/系统生成合同_", contractId, ".pdf")
+		tmpErr = services.Html2Pdf(contractHtml, pdfPath)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		defer os.Remove(pdfPath)
+
+		//randStr := utils.GetRandStringNoSpecialChar(28)
+		fileName := contractDetail.ContractCode + ".pdf"
+		//上传到阿里云
+		resourceUrl, tmpErr := services.UploadAliyun(fileName, pdfPath)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		tmpErr = contract.AddContractPdf(contractId, resourceUrl)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	//修改客户名称
+	{
+		//根据社会信用码获取客户信息
+		companyInfo, tmpErr := company.GetCompanyByCreditCode(contractDetail.CreditCode)
+		//如果查询异常,且并不是在系统中找不到该社会信用码,那么就异常返回
+		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			err = tmpErr
+			return
+		}
+		//如果客户信息不为空的情况下,那么去校验客户名称
+		if companyInfo != nil {
+			if companyInfo.CompanyName != contractDetail.CompanyName || companyInfo.Province != contractDetail.Province || companyInfo.City != contractDetail.City {
+				companyInfo.CompanyName = contractDetail.CompanyName
+				companyInfo.Province = contractDetail.Province
+				companyInfo.City = contractDetail.City
+				companyInfo.Address = contractDetail.Address
+
+				tmpErr = company.ModifyCompany(companyInfo)
+				if tmpErr != nil {
+					err = tmpErr
+					return
+				}
+
+				//新增客户日志
+				{
+					companyLog := new(company_log.CompanyLog)
+					companyLog.CompanyId = companyInfo.CompanyId
+					companyLog.CompanyName = companyInfo.CompanyName
+					companyLog.CreditCode = companyInfo.CreditCode
+					companyLog.Address = companyInfo.Address
+					companyLog.City = companyInfo.City
+					companyLog.Province = companyInfo.Province
+					companyLog.LastUpdatedTime = companyInfo.LastUpdatedTime
+					companyLog.AdminId = 0
+					companyLog.LogType = "modify"
+					companyLog.LogCreateTime = time.Now()
+					go company_log.AddCompanyLog(companyLog)
+				}
+			}
+		}
+
+	}
+	return
+}

+ 77 - 0
services/flow/flow.go

@@ -0,0 +1,77 @@
+package flow
+
+import (
+	"errors"
+	"hongze/hongze_mobile_admin/models/tables/admin"
+	"hongze/hongze_mobile_admin/models/tables/approval_flow"
+	"hongze/hongze_mobile_admin/models/tables/approval_flow_node"
+	"strings"
+)
+
+//获取审批工作流(基础)
+func GetApprovalFlow(flowId int) (approvalFlow *approval_flow.ApprovalFlowItem, err error) {
+	approvalFlow, err = approval_flow.GetByFlowItemId(flowId)
+	if err != nil {
+		err = errors.New("查询审批流程失败:Err:" + err.Error())
+		return
+	}
+	approvalFlowNodeList, err := approval_flow_node.GetListByFlowId(approvalFlow.FlowId, approvalFlow.CurrVersion)
+	if err != nil {
+		err = errors.New("查询审批节点失败:Err:" + err.Error())
+		return
+	}
+	for _, approvalFlowNode := range approvalFlowNodeList {
+		userList, tmpErr := getAdminListByUserType(approvalFlowNode.UserType, approvalFlowNode.User, approvalFlow.ProductId)
+		if tmpErr != nil {
+			err = errors.New("获取审批人失败:Err:" + tmpErr.Error())
+			return
+		}
+		for _, user := range userList {
+			userInfo := approval_flow_node.User{
+				AdminId:      user.AdminId,
+				Name:         user.RealName,
+				RoleTypeCode: user.RoleTypeCode,
+				Mobile:       user.Mobile,
+			}
+			approvalFlowNode.UserList = append(approvalFlowNode.UserList, userInfo)
+		}
+	}
+	approvalFlow.NodeList = approvalFlowNodeList
+
+	return
+}
+
+//获取审批人信息
+func getAdminListByUserType(userType, user string, productId int) (adminList []*admin.Admin, err error) {
+	switch userType {
+	case "user":
+		adminList, err = admin.GetAdminListByIds(user)
+		return
+	case "manager":
+		roleTypeCode := ``
+		if productId == 1 {
+			roleTypeCode = `ficc_admin`
+		} else if productId == 1 {
+			roleTypeCode = `rai_admin`
+		}
+		if roleTypeCode == `` {
+			err = errors.New("找不到对应的主管")
+			return
+		}
+		adminList, err = admin.GetAdminListByRoleCode(roleTypeCode)
+		return
+	case "role": //角色类型
+		roleSlice := strings.Split(user, ",")
+		for _, roleTypeCode := range roleSlice {
+			list, tmpErr := admin.GetAdminListByRoleCode(roleTypeCode)
+			if tmpErr != nil {
+				err = tmpErr
+				return
+			}
+			adminList = append(adminList, list...)
+		}
+		return
+	}
+	err = errors.New("查找审批人信息异常")
+	return
+}

+ 91 - 0
services/oss.go

@@ -0,0 +1,91 @@
+package services
+
+import (
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"os"
+	"time"
+
+	"hongze/hongze_mobile_admin/utils"
+)
+
+/*
+上传demo
+func init() {
+	fmt.Println("start")
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName :=  randStr + ".jpg"
+	fmt.Println("fileName:",fileName)
+	fpath:="./1.png"
+	resourceUrl,err:=UploadAliyun(fileName,fpath)
+	if err!=nil {
+		fmt.Println("UploadAliyun Err:",err.Error())
+		return
+	}
+	fmt.Println("resourceUrl:",resourceUrl)
+	fmt.Println("end")
+}
+*/
+
+//图片上传到阿里云
+func UploadAliyun(filename, filepath string) (string, error) {
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return "1", err
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return "2", err
+	}
+	path := utils.Upload_dir + time.Now().Format("200601/20060102/")
+	path += filename
+	err = bucket.PutObjectFromFile(path, filepath)
+	if err != nil {
+		return "3", err
+	}
+	path = utils.Imghost + path
+	return path, err
+}
+
+//音频上传到阿里云
+func UploadAudioAliyun(filename, filepath string) (string, error) {
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return "1", err
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return "2", err
+	}
+	path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+	path += filename
+	err = bucket.PutObjectFromFile(path, filepath)
+	if err != nil {
+		return "3", err
+	}
+	path = utils.Imghost + path
+	return path, err
+}
+
+//视频上传到阿里云
+func UploadVideoAliyun(filename, filepath, savePath string) error {
+	defer func() {
+		os.Remove(filepath)
+	}()
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return err
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return err
+	}
+	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+	//path += filename
+	err = bucket.PutObjectFromFile(savePath, filepath)
+	if err != nil {
+		return err
+	}
+	//path = utils.Imghost + path
+	//return path,err
+	return err
+}

+ 1045 - 0
services/word.go

@@ -0,0 +1,1045 @@
+package services
+
+import (
+	"baliance.com/gooxml/color"
+	"baliance.com/gooxml/document"
+	"baliance.com/gooxml/measurement"
+	"baliance.com/gooxml/schema/soo/ofc/sharedTypes"
+	"baliance.com/gooxml/schema/soo/wml"
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	wkhtml "github.com/SebastiaanKlippert/go-wkhtmltopdf"
+	"github.com/shopspring/decimal"
+	contractReq "hongze/hongze_mobile_admin/models/request/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract"
+	"hongze/hongze_mobile_admin/models/tables/contract_service_detail"
+	"hongze/hongze_mobile_admin/models/tables/contract_service_template"
+	"hongze/hongze_mobile_admin/models/tables/contract_template"
+	"hongze/hongze_mobile_admin/utils"
+	"html/template"
+	"reflect"
+	"strconv"
+	"strings"
+)
+
+type TableData struct {
+	List []TableRow `json:"table";description:"列数据"`
+}
+
+type TableRow struct {
+	RowList []TableCel `json:"row";description:"列数据"`
+}
+
+type TableCel struct {
+	Value         string  `json:"value";description:"展示的数据"`
+	ColumnSpan    int     `json:"column_span";description:"需要合同的列数量"`
+	RowSpan       int     `json:"row_span";description:"需要合同的行数量"`
+	IsMerged      bool    `json:"is_merged";description:"是否需要上下行合并"`
+	IsFirstMerged bool    `json:"is_first_merged";description:"是否是第一次合并上下行"`
+	Background    string  `json:"background";description:"背景色"`
+	IsBold        bool    `json:"is_bold";description:"是否加粗显示"`
+	TextAlign     string  `json:"text_align";description:"对齐方式"`
+	FontSize      float64 `json:"font_size";description:"字体大小"`
+	WidthPercent  float64 `json:"width_percent";description:"单元格宽度占整个表格的百分比"`
+}
+
+//获取颜色配置
+func getColorConf(background string) (foreground color.Color) {
+	switch background {
+	case "slate_gray": //石板灰
+		foreground = color.SlateGray
+	case "light_slate_gray": //浅石板灰
+		foreground = color.LightSlateGray
+	case "light_gray": //浅灰
+		foreground = color.LightGray
+	case "gray": //灰色
+		foreground = color.Gray
+	case "gray_1": //灰色_1(中浅灰)
+		foreground = color.RGB(uint8(215), uint8(215), uint8(215))
+	case "gray_2": //灰色_2(浅灰)
+		foreground = color.RGB(uint8(241), uint8(241), uint8(241))
+	case "dim_gray": //暗灰色
+		foreground = color.DimGray
+	case "dark_slate_gray": //深灰色
+		foreground = color.DarkSlateGray
+
+	default:
+		foreground = color.LightGray
+	}
+	return
+}
+
+func getTextAlignConf(textAlign string) (align wml.ST_Jc) {
+	switch textAlign {
+	case "left": //居左
+		align = wml.ST_JcLeft
+	case "center": //居中
+		align = wml.ST_JcCenter
+	case "right": //居右
+		align = wml.ST_JcRight
+	case "both": //
+		align = wml.ST_JcBoth
+	default:
+		align = wml.ST_JcLeft
+	}
+	return
+}
+
+//生成word
+func GenerateWord(contractDetail *contract.ContractDetail) (err error) {
+	wordTemplatePath := getWordPath(contractDetail.TemplateId)
+	if wordTemplatePath == "" {
+		err = errors.New("找不到对应的合同模板")
+		return
+	}
+
+	doc, err := document.Open(wordTemplatePath)
+	if err != nil {
+		fmt.Println("error opening document: %s", err)
+		return
+	}
+	paragraphs := []document.Paragraph{}
+	for _, p := range doc.Paragraphs() {
+		paragraphs = append(paragraphs, p)
+	}
+
+	// This sample document uses structured document tags, which are not common
+	// except for in document templates.  Normally you can just iterate over the
+	// document's paragraphs.
+	for _, sdt := range doc.StructuredDocumentTags() {
+		for _, p := range sdt.Paragraphs() {
+			paragraphs = append(paragraphs, p)
+		}
+	}
+	doc.AddParagraph()
+	for _, p := range paragraphs {
+		for _, r := range p.Runs() {
+			switch r.Text() {
+			case "{{address}}":
+				// ClearContent clears both text and line breaks within a run,
+				// so we need to add the line break back
+				r.ClearContent()
+				address := contractDetail.Province + contractDetail.City + contractDetail.Address
+				r.AddText(address)
+				//r.AddBreak()
+
+				//para := doc.InsertParagraphBefore(p)
+				//para.AddRun().AddText("Mr.")
+				//para.SetStyle("Name") // Name is a default style in this template file
+				//
+				//para = doc.InsertParagraphAfter(p)
+				//para.AddRun().AddText("III")
+				//para.SetStyle("Name")
+
+			case "{{postcode}}":
+				r.ClearContent()
+				r.AddText(contractDetail.Postcode)
+			case "{{phone}}":
+				r.ClearContent()
+				r.AddText(contractDetail.Phone)
+			case "{{fax}}":
+				r.ClearContent()
+				r.AddText(contractDetail.Fax)
+			case "{{remark}}":
+				r.ClearContent()
+				remark := contractDetail.Remark
+				if remark == "" {
+					remark = "无"
+				}
+				r.AddText(remark)
+			case "{{start_date}}":
+				r.ClearContent()
+				r.AddText(contractDetail.StartDate.Format("2006 年 01 月 02 日"))
+			case "{{end_date}}":
+				r.ClearContent()
+				r.AddText(contractDetail.EndDate.Format("2006 年 01 月 02 日"))
+			case "{{num_year}}":
+				r.ClearContent()
+				//合同结束日期与合同开始日期的时间差(小时差)
+				newDecimal := decimal.NewFromFloat(contractDetail.EndDate.Sub(contractDetail.StartDate).Hours())
+				//分母为365天 * 24 小时
+				newDecimal2 := decimal.NewFromInt(24 * 365)
+				//计算出来相差多少年,保留一位小数(四舍五入)
+				numYearDecimal := newDecimal.Div(newDecimal2).Round(1)
+				//定义最小年份差,不能小于0.1年
+				minDecimal := decimal.NewFromFloat(0.1)
+				//如果计算出来的年份差小于0.1年,那么该年份差就赋值 0.1年
+				if numYearDecimal.LessThan(minDecimal) {
+					numYearDecimal = minDecimal
+				}
+				//cnYear, cnErr := utils.ConvertNumToCn(numYearDecimal.String())
+				//if cnErr != nil {
+				//	err = cnErr
+				//	return
+				//}
+				r.AddText(numYearDecimal.String())
+			case "{{price}}":
+				r.ClearContent()
+				priceStr := ""
+				//originalPrice := strconv.FormatFloat(contractDetail.OriginalPrice, 'E', -1, 64)
+
+				//优惠前金额(小写)
+				newDecimal := decimal.NewFromFloat(contractDetail.OriginalPrice)
+				originalPrice := newDecimal.String()
+				priceStr += "小写:" + originalPrice + ","
+
+				//优惠前金额(大写)
+				originalCnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.OriginalPrice)
+				if cnyErr != nil {
+					err = cnyErr
+					return
+				}
+				priceStr += "大写:" + originalCnyPrice
+
+				//如果实际支付金额与订单原金额不符
+				if contractDetail.OriginalPrice != contractDetail.Price {
+					//优惠后的金额(小写)
+					newDecimal := decimal.NewFromFloat(contractDetail.Price)
+					price := newDecimal.String()
+					priceStr += ",经甲乙双方友好协商,优惠至:" + price + "元,"
+
+					//优惠后的金额(大写)
+					cnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.Price)
+					if cnyErr != nil {
+						err = cnyErr
+						return
+					}
+					priceStr += "大写:" + cnyPrice
+				}
+
+				r.AddText(priceStr)
+
+			case "{{pay_remark}}":
+				r.ClearContent()
+				r.AddText(contractDetail.PayRemark)
+			case "{{company_name}}":
+				r.ClearContent()
+				r.AddText(contractDetail.CompanyName)
+				//r.AddBreak()
+			case "{{services}}":
+				r.ClearContent()
+				//赋值当前段落
+				nowParagraph := p
+
+				for i := len(contractDetail.Service) - 1; i >= 0; i-- {
+					//表格数据
+					var tableDataList TableData
+					//表头备注信息
+					tableTitle := ""
+
+					item := contractDetail.Service[i]
+					//表格数据
+					if item.HasDetail == "是" && len(item.DetailList) > 0 {
+						//表格每行数据切片
+						tableRowList := make([]TableRow, 0)
+
+						//遍历获取table行数据
+						for j := 0; j < len(item.DetailList); j++ {
+							//列数据样式初始化
+							isBold := false
+							backgrandColor := ""
+							fontSize := 10.0
+
+							//表头数据样式
+							if j == 0 {
+								isBold = true
+								backgrandColor = "gray_2"
+								fontSize = 12.0
+							}
+
+							//获取每一列的数据
+							tmpCellList, colErr := getColList(item.DetailList[j])
+							if colErr != nil {
+								err = colErr
+								return
+							}
+
+							//定义生成table列数据切片
+							tableCelList := make([]TableCel, 0)
+							lenCell := len(tmpCellList)
+							for k := 0; k < len(tmpCellList); k++ {
+								//计算出来每一列的宽度占比 start
+								//总宽度
+								newDecimal := decimal.NewFromFloat(100)
+								//总列数
+								newDecimal2 := decimal.NewFromInt(int64(lenCell))
+								//计算出来每一列的宽度占比(四舍五入)
+								widthPercent, _ := newDecimal.Div(newDecimal2).Round(3).Float64()
+								//if !ok {
+								//	err = errors.New("word普通数据表格宽度百分比计算失败")
+								//	return
+								//}
+								//计算出来每一列的宽度占比 end
+
+								tableCel := TableCel{
+									Value:     tmpCellList[k],
+									TextAlign: "center",
+									//ColumnSpan   int     `json:"column_span";description:"需要合同的列数量"`
+									//IsMerged     bool    `json:"is_merged";description:"是否需要上下行合并"`
+									Background:   backgrandColor,
+									IsBold:       isBold,
+									FontSize:     fontSize,
+									WidthPercent: widthPercent,
+								}
+								tableCelList = append(tableCelList, tableCel)
+							}
+
+							//将每行数据插入到table行数据切片之中
+							tableRow := TableRow{
+								RowList: tableCelList,
+							}
+							tableRowList = append(tableRowList, tableRow)
+						}
+						//赋值table表格数据
+						tableDataList.List = tableRowList
+						tableTitle = "依照《弘则研究FICC客户服务列表2021》中 小套餐 的服务内容,详细如下:"
+					} else {
+						//获取预设的表格数据
+						contractServiceTemplate, tmpErr := contract_service_template.GetContractServiceTemplateById(item.ServiceTemplateId)
+						if tmpErr != nil {
+							err = tmpErr
+							return
+						}
+
+						//赋值table表格数据
+						jsonStr := contractServiceTemplate.TableValue
+						err = json.Unmarshal([]byte(jsonStr), &tableDataList)
+						if err != nil {
+							return
+						}
+						//表头备注信息
+						tableTitle = contractServiceTemplate.Remark
+					}
+
+					//往word中添加表格数据
+					tmpParagraph, tmpErr := addTable(tableTitle, tableDataList, doc, nowParagraph)
+					if tmpErr != nil {
+						err = tmpErr
+						return
+					}
+					//fmt.Println("nowParagraph:", nowParagraph, "tmpParagraph:", tmpParagraph)
+					//fmt.Println("doc:", doc.Paragraphs())
+					//fmt.Println("==========:")
+					nowParagraph = tmpParagraph
+				}
+			default:
+				//fmt.Println("not modifying", r.Text())
+			}
+		}
+	}
+	doc.SaveToFile(fmt.Sprint("./static/word/系统生成合同", contractDetail.ContractId, ".docx"))
+	return
+}
+
+//添加表格数据
+func addTable(title string, tableDataList TableData, doc *document.Document, paragraph document.Paragraph) (nowParagraph document.Paragraph, err error) {
+	//fmt.Println("表头名称:", title)
+	//插入一个新的段落
+	nowParagraph = doc.InsertParagraphBefore(paragraph)
+	nowRun := nowParagraph.AddRun()
+	nowRun.AddBreak()
+	//if title != "" {
+	//	fmt.Println("表头名称:", title)
+	//	nowRun.Properties().SetSize(11)
+	//	nowRun.Properties().SetBold(true)
+	//	nowRun.AddText(title)
+	//	nowRun.AddBreak()
+	//}
+
+	//再次插入一个新段落
+	//_ = doc.InsertParagraphAfter(nowParagraph)
+	//表格数据
+
+	{
+		table := doc.InsertTableAfter(nowParagraph)
+		//设置表格宽度
+		table.Properties().SetWidth(6.5 * measurement.Inch)
+		//表格宽度设置为自动
+		//table.Properties().SetWidthAuto()
+
+		//边框
+		borders := table.Properties().Borders()
+		// thin borders
+		borders.SetAll(wml.ST_BorderSingle, color.Auto, measurement.Zero)
+
+		//表格数据
+		rowList := tableDataList.List
+		//每一列合并单元格状态map
+		rowIsMeged := make(map[int]bool)
+
+		//table.Properties().W
+		for i := 0; i < len(rowList); i++ {
+			//创建新的一行
+			row := table.AddRow()
+			//设置行高,第二个参数是设置固定值还是自动
+			row.Properties().SetHeight(30*measurement.Point, wml.ST_HeightRuleAtLeast)
+
+			//遍历列数据
+			rowDataList := rowList[i].RowList
+			if rowDataList != nil {
+				for j := 0; j < len(rowDataList); j++ {
+					//当前列是否合并
+					var isMeged bool
+					isMeged, ok := rowIsMeged[j]
+					if !ok {
+						rowIsMeged[j] = false
+						isMeged = false
+					}
+
+					cell := row.AddCell()
+					cellPara := cell.AddParagraph()
+					run := cellPara.AddRun()
+					//列数据
+					cellData := rowDataList[j]
+
+					//如果合并列大于0,那么就合并列
+					if cellData.ColumnSpan > 0 {
+						// column span / merged cells
+						cell.Properties().SetColumnSpan(cellData.ColumnSpan)
+						//_ = row.AddCell()
+					}
+
+					//如果指定了上下单元格合并,那么去合并上下单元格
+					if cellData.IsMerged {
+						//将当前合并单元格状态调整为true
+						rowIsMeged[j] = true
+						//合并单元格类型
+						var mergeVal wml.ST_Merge
+
+						if isMeged { //如果上一层已经是合并了,那么这一层是继续合并
+							mergeVal = wml.ST_MergeContinue
+						} else { //如果上一层不是合并,那么这一层是开始合并
+							mergeVal = wml.ST_MergeRestart
+						}
+						cell.Properties().SetVerticalMerge(mergeVal)
+					} else {
+						//将当前合并单元格状态调整为false,这样后续如果再次碰到合并单元格操作,就是重新开始合并了
+						rowIsMeged[j] = false
+					}
+
+					//背景色
+					if cellData.Background != "" {
+						cell.Properties().SetShading(wml.ST_ShdSolid, getColorConf(cellData.Background), color.Auto)
+					}
+
+					//填充内容(文字)垂直对齐方式
+					cell.Properties().SetVerticalAlignment(wml.ST_VerticalJcCenter)
+
+					//将单元格设置为宽度百分比
+					if cellData.WidthPercent > 0 {
+						cell.Properties().SetWidthPercent(cellData.WidthPercent)
+					}
+
+					//文字排版(居中、左、右)
+					if cellData.TextAlign != "" {
+						cellPara.Properties().SetAlignment(getTextAlignConf(cellData.TextAlign))
+						//cellPara.Properties().SetAlignment(wml.ST_JcLeft)
+					}
+
+					//cell.Properties().SetAli
+					//设置是否加粗
+					run.Properties().SetBold(cellData.IsBold)
+
+					//设置字体大小
+					fontSize := 10.0
+					if cellData.FontSize > 0 {
+						fontSize = cellData.FontSize
+					}
+					run.Properties().SetSize(measurement.Distance(fontSize * measurement.Point))
+
+					//设置段落间的间距
+					cellPara.Properties().Spacing().SetLineSpacing(measurement.Distance(1.4*fontSize*measurement.Point), wml.ST_LineSpacingRuleAuto)
+					//设置段前间距
+					cellPara.Properties().Spacing().SetBefore(measurement.Distance(0.9 * fontSize * measurement.Point))
+					//设置段后间距
+					cellPara.Properties().Spacing().SetAfter(measurement.Distance(0.5 * fontSize * measurement.Point))
+
+					//设置字体
+					run.Properties().SetFontFamily("宋体")
+
+					//设置排序
+					run.Properties().SetVerticalAlignment(sharedTypes.ST_VerticalAlignRunBaseline)
+
+					//设置显示的文字
+					if cellData.Value != "" {
+						strSlice := strings.Split(cellData.Value, "<br/>")
+						for s := 0; s < len(strSlice); s++ {
+							if s > 0 {
+								run.AddBreak()
+							}
+							run.AddText(strSlice[s])
+						}
+					} else {
+						run.AddText("")
+					}
+				}
+
+			}
+		}
+
+	}
+	return
+}
+
+//获取生成word的docx模板文件
+func getWordPath(templateId int) string {
+	var path string
+	switch templateId {
+	case 1:
+		path = "./static/word/template_1.docx"
+	case 2:
+		path = "./static/word/template_2.docx"
+	}
+	return path
+}
+
+//html转pdf数据样式
+type html2pdfData struct {
+	CompanyName  string `description:"甲方名称"`
+	ContractCode string `description:"合同编号"`
+	Address      string `description:"甲方地址"`
+	Postcode     string `description:"甲方邮编"`
+	Phone        string `description:"甲方电话"`
+	Fax          string `description:"传真"`
+	Remark       string `description:"备注"`
+	PayRemark    string `description:"支付备注"`
+	StartDate    string `description:"合同开始日期"`
+	EndDate      string `description:"合同结束日期"`
+	NumYear      string `description:"合同有效期"`
+	Price        string `description:"支付金额"`
+	TableHtml    string `description:"表格数据"`
+}
+
+//获取合同样式预览的html
+func GetHtmlByContractDetail(contractDetail *contract.ContractDetail, htmlType string) (contractHtml string, err error) {
+	contractTemplate, err := contract_template.GetContractTemplateByTemplateId(contractDetail.TemplateId)
+	if err != nil {
+		return
+	}
+	htmlTpl := contractTemplate.Html
+	if htmlType == "pdf" {
+		htmlTpl = contractTemplate.PdfHtml
+	}
+	myTpl := template.Must(template.New("contract").Parse(htmlTpl))
+
+	//地址
+	address := contractDetail.Province + contractDetail.City + contractDetail.Address
+	data := html2pdfData{
+		CompanyName:  contractDetail.CompanyName,
+		ContractCode: contractDetail.ContractCode,
+		Address:      address,
+		Postcode:     contractDetail.Postcode,
+		Phone:        contractDetail.Phone,
+		Fax:          contractDetail.Fax,
+		Remark:       contractDetail.Remark,
+		PayRemark:    contractDetail.PayRemark,
+		StartDate:    contractDetail.StartDate.Format("2006年01月02日"),
+		EndDate:      contractDetail.EndDate.Format("2006年01月02日"),
+	}
+
+	if data.Postcode == "" {
+		data.Postcode = "无"
+	}
+	if data.Fax == "" {
+		data.Fax = "无"
+	}
+	if data.Phone == "" {
+		data.Phone = "无"
+	}
+	if data.PayRemark == "" {
+		data.PayRemark = "无"
+	}
+	if data.Remark == "" {
+		data.Remark = "无"
+	}
+
+	//合同有效期
+	{
+		//合同结束日期与合同开始日期的时间差(小时差)
+		newDecimal := decimal.NewFromFloat(contractDetail.EndDate.Sub(contractDetail.StartDate).Hours())
+		//分母为365天 * 24 小时
+		newDecimal2 := decimal.NewFromInt(24 * 365)
+		//计算出来相差多少年,保留一位小数(四舍五入)
+		numYearDecimal := newDecimal.Div(newDecimal2).Round(1)
+		//定义最小年份差,不能小于0.1年
+		minDecimal := decimal.NewFromFloat(0.1)
+		//如果计算出来的年份差小于0.1年,那么该年份差就赋值 0.1年
+		if numYearDecimal.LessThan(minDecimal) {
+			numYearDecimal = minDecimal
+		}
+
+		//合同有效期
+		data.NumYear = numYearDecimal.String()
+	}
+
+	//合同金额
+	{
+		priceStr := ""
+		//originalPrice := strconv.FormatFloat(contractDetail.OriginalPrice, 'E', -1, 64)
+
+		//优惠前金额(小写)
+		newDecimal := decimal.NewFromFloat(contractDetail.OriginalPrice)
+		originalPrice := newDecimal.String()
+		priceStr += "小写:" + originalPrice + ","
+
+		//优惠前金额(大写)
+		originalCnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.OriginalPrice)
+		if cnyErr != nil {
+			err = cnyErr
+			return
+		}
+		priceStr += "大写:" + originalCnyPrice
+
+		//如果实际支付金额与订单原金额不符
+		if contractDetail.OriginalPrice != contractDetail.Price {
+			//优惠后的金额(小写)
+			newDecimal := decimal.NewFromFloat(contractDetail.Price)
+			price := newDecimal.String()
+			priceStr += ",经甲乙双方友好协商,优惠至:" + price + "元,"
+
+			//优惠后的金额(大写)
+			cnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.Price)
+			if cnyErr != nil {
+				err = cnyErr
+				return
+			}
+			priceStr += "大写:" + cnyPrice
+		}
+		data.Price = priceStr
+	}
+
+	buf := new(bytes.Buffer) //实现了读写方法的可变大小的字节缓冲
+	tplErr := myTpl.Execute(buf, data)
+	if tplErr != nil {
+		err = tplErr
+		return
+	}
+	contractHtml = buf.String()
+
+	//服务内容
+	{
+		tableStr := ""
+		tableDataSlice := make([]TableData, 0)
+		tableTitleSlice := make([]string, 0)
+		title := ""
+		if contractDetail.ProductId == 1 {
+			title = "依照《【弘则研究】FICC客户客户服务列表2021》中 "
+		} else {
+			title = "依照《【弘则研究】私募客户客户服务列表2021》中 "
+		}
+		for i := 0; i < len(contractDetail.Service); i++ {
+			//表格数据
+			var tableDataList TableData
+
+			item := contractDetail.Service[i]
+			//表头备注信息
+			tableTitleSlice = append(tableTitleSlice, item.Title)
+
+			//表格数据
+			if item.HasDetail == "是" && len(item.DetailList) > 0 {
+				//表格每行数据切片
+				tableRowList := make([]TableRow, 0)
+
+				//遍历获取table行数据
+				for j := 0; j < len(item.DetailList); j++ {
+					//列数据样式初始化
+					isBold := false
+					backgrandColor := ""
+					fontSize := 13.0
+
+					//表头数据样式
+					if j == 0 {
+						isBold = true
+						backgrandColor = "gray_2"
+						fontSize = 13.0
+					}
+
+					//获取每一列的数据
+					tmpCellList, colErr := getColList(item.DetailList[j])
+					if colErr != nil {
+						err = colErr
+						return
+					}
+
+					//定义生成table列数据切片
+					tableCelList := make([]TableCel, 0)
+					lenCell := len(tmpCellList)
+					for k := 0; k < len(tmpCellList); k++ {
+						//默认30%的宽度,如果不是第一列,那么需要额外计算
+						widthPercent := 30.0
+						if k > 0 {
+							//计算出来每一列的宽度占比 start
+							//总宽度
+							newDecimal := decimal.NewFromFloat(70)
+							//总列数
+							newDecimal2 := decimal.NewFromInt(int64(lenCell) - 1)
+							//计算出来每一列的宽度占比(四舍五入)
+							tmpWidthPercent, _ := newDecimal.Div(newDecimal2).Round(3).Float64()
+							//if !ok {
+							//	err = errors.New("word普通数据表格宽度百分比计算失败")
+							//	return
+							//}
+							widthPercent = tmpWidthPercent
+							//计算出来每一列的宽度占比 end
+						}
+
+						tableCel := TableCel{
+							Value:     tmpCellList[k],
+							TextAlign: "center",
+							//ColumnSpan   int     `json:"column_span";description:"需要合同的列数量"`
+							//IsMerged     bool    `json:"is_merged";description:"是否需要上下行合并"`
+							Background:   backgrandColor,
+							IsBold:       isBold,
+							FontSize:     fontSize,
+							WidthPercent: widthPercent,
+						}
+						tableCelList = append(tableCelList, tableCel)
+					}
+
+					//将每行数据插入到table行数据切片之中
+					tableRow := TableRow{
+						RowList: tableCelList,
+					}
+					tableRowList = append(tableRowList, tableRow)
+				}
+				//赋值table表格数据
+				tableDataList.List = tableRowList
+			} else {
+				//获取预设的表格数据
+				contractServiceTemplate, tmpErr := contract_service_template.GetContractServiceTemplateById(item.ServiceTemplateId)
+				if tmpErr != nil {
+					err = tmpErr
+					return
+				}
+
+				//赋值table表格数据
+				jsonStr := contractServiceTemplate.TableValue
+				tmpEerr := json.Unmarshal([]byte(jsonStr), &tableDataList)
+				if tmpEerr != nil {
+					err = tmpEerr
+					return
+				}
+			}
+
+			tableDataSlice = append(tableDataSlice, tableDataList)
+
+		}
+		titleStr := strings.Join(tableTitleSlice, "、")
+		title += titleStr + "的服务内容,详细如下:"
+
+		if htmlType == "pdf" {
+			tableStr += `<p style="">` + title + `</p>`
+		} else {
+			tableStr = `<p style="font-size: 13pt;  line-height: 40px">` + title + `</p>`
+		}
+
+		for _, tableDataList := range tableDataSlice {
+			//往word中添加表格数据
+			if htmlType == "pdf" {
+				tableStr += getTableStrByPdf(tableDataList)
+			} else {
+				tableStr += getTableStr(tableDataList)
+			}
+		}
+		data.TableHtml = tableStr
+	}
+	//fmt.Println("TableHtml:", data.TableHtml)
+	contractHtml = strings.Replace(contractHtml, `\{\{\{TableHtml\}\}\}`, data.TableHtml, -1)
+	return
+	//生成pdf
+	//pdfPath := fmt.Sprint("./static/word/系统生成合同", contractDetail.ContractCode, ".pdf")
+	//err = Html2Pdf(contractHtml, pdfPath)
+	//if err != nil {
+	//	return
+	//}
+	////defer func() {
+	////	//删除对应的Pdf
+	////	os.Remove(pdfPath)
+	////}()
+	//
+	//return
+}
+
+//生成合同服务的预览表格html代码
+func getTableStr(tableDataList TableData) (tableStr string) {
+	//如果表格需要分页,那么在table的style里面添加该配置:page-break-inside: avoid !important
+	tableStr += `<table style="width: 100%;border-collapse: collapse;font-size: 13pt;margin-bottom:30px;page-break-inside: avoid !important;"><tbody>`
+	rowList := tableDataList.List
+	for i := 0; i < len(rowList); i++ {
+		//创建新的一行
+		tableStr += `<tr style="`
+		tableStr += `page-break-before: always;page-break-after: always;page-break-inside: avoid !important;`
+
+		//background-color: #F0F2F5;
+		tableStr += `">`
+
+		//<td style="border-right:1px solid #808181;border-bottom:1px solid #808181;padding: 15px 10px;font-weight: bold;" colspan="5">2.市场跟踪类报告</td>
+
+		//row := table.AddRow()
+		////设置行高,第二个参数是设置固定值还是自动
+		//row.Properties().SetHeight(30*measurement.Point, wml.ST_HeightRuleAtLeast)
+		//
+		//遍历列数据
+		rowDataList := rowList[i].RowList
+
+		cellStr := ""
+		if rowDataList != nil {
+			for j := 0; j < len(rowDataList); j++ {
+				//当前列是否合并
+				//                <td style="font-weight: bold;" colspan="5">2.市场跟踪类报告</td>
+				//                <td style="" align="center" rowspan="3">市场跟踪</td>
+				//                <td style="" align="center">市场估值</td>
+
+				tdStr := `<td `
+				//单元格样式
+				styleStr := `style="`
+				styleStr += `border:1px solid #808181;padding: 15px 10px;line-height: 1.5;`
+
+				//其他参数
+				cellOtherStr := ` valign="middle" `
+
+				//列数据
+				cellData := rowDataList[j]
+
+				//如果合并列大于0,那么就合并列
+				if cellData.ColumnSpan > 0 {
+					// column span / merged cells
+					cellOtherStr += ` colspan="` + strconv.Itoa(cellData.ColumnSpan) + `" `
+				}
+
+				//如果指定了上下单元格合并,那么去合并上下单元格
+				if cellData.IsMerged {
+					if cellData.IsFirstMerged {
+						cellOtherStr += ` rowspan="` + strconv.Itoa(cellData.RowSpan) + `" `
+					} else {
+						//如果是合并行,且不是第一行,那么就退出当前单元格循环,进入下一个循环
+						continue
+					}
+				}
+
+				//背景色
+				if cellData.Background != "" {
+					styleStr += `background-color: #F0F2F5;`
+				}
+
+				//将单元格设置为宽度百分比
+				if cellData.WidthPercent > 0 {
+					widthDecimal := decimal.NewFromFloat(cellData.WidthPercent)
+					cellOtherStr += ` width="` + widthDecimal.String() + `%" `
+				}
+
+				//文字排版(居中、左、右)
+				if cellData.TextAlign != "" {
+					cellOtherStr += ` align="` + cellData.TextAlign + `" `
+				}
+
+				//cell.Properties().SetAli
+				//设置是否加粗
+				if cellData.IsBold {
+					styleStr += `font-weight:bold;`
+				}
+
+				//设置字体大小
+				fontSize := 10.0
+				if cellData.FontSize > 0 {
+					fontSize = cellData.FontSize
+				}
+				fontDecimal := decimal.NewFromFloat(fontSize)
+				styleStr += `font-size: ` + fontDecimal.String() + `pt;`
+
+				bodyStr := cellData.Value
+
+				styleStr += `" `
+
+				cellStr += tdStr + styleStr + cellOtherStr + `>` + bodyStr + `</td>`
+			}
+		}
+		tableStr += cellStr + `</tr>`
+	}
+	tableStr += `</tbody></table>`
+
+	return
+}
+
+//生成合同服务的pdf表格html代码
+func getTableStrByPdf(tableDataList TableData) (tableStr string) {
+	//如果表格需要分页,那么在table的style里面添加该配置:page-break-inside: avoid !important
+	tableStr += `<table style="width: 100%;border-collapse: collapse;margin-top:10pt;page-break-inside: avoid !important;"><tbody>`
+	rowList := tableDataList.List
+	for i := 0; i < len(rowList); i++ {
+		//创建新的一行
+		tableStr += `<tr style="`
+		tableStr += `page-break-before: always;page-break-after: always;page-break-inside: avoid !important;`
+
+		//background-color: #F0F2F5;
+		tableStr += `">`
+
+		//<td style="border-right:1px solid #808181;border-bottom:1px solid #808181;padding: 15px 10px;font-weight: bold;" colspan="5">2.市场跟踪类报告</td>
+
+		//row := table.AddRow()
+		////设置行高,第二个参数是设置固定值还是自动
+		//row.Properties().SetHeight(30*measurement.Point, wml.ST_HeightRuleAtLeast)
+		//
+		//遍历列数据
+		rowDataList := rowList[i].RowList
+
+		cellStr := ""
+		if rowDataList != nil {
+			for j := 0; j < len(rowDataList); j++ {
+				//当前列是否合并
+				//                <td style="font-weight: bold;" colspan="5">2.市场跟踪类报告</td>
+				//                <td style="" align="center" rowspan="3">市场跟踪</td>
+				//                <td style="" align="center">市场估值</td>
+
+				tdStr := `<td `
+				//单元格样式
+				styleStr := `style="`
+				styleStr += `border:1px solid #808181;padding:4pt 10pt;`
+
+				//其他参数
+				cellOtherStr := ` valign="middle" `
+
+				//列数据
+				cellData := rowDataList[j]
+
+				//如果合并列大于0,那么就合并列
+				if cellData.ColumnSpan > 0 {
+					// column span / merged cells
+					cellOtherStr += ` colspan="` + strconv.Itoa(cellData.ColumnSpan) + `" `
+				}
+
+				//如果指定了上下单元格合并,那么去合并上下单元格
+				if cellData.IsMerged {
+					if cellData.IsFirstMerged {
+						cellOtherStr += ` rowspan="` + strconv.Itoa(cellData.RowSpan) + `" `
+					} else {
+						//如果是合并行,且不是第一行,那么就退出当前单元格循环,进入下一个循环
+						continue
+					}
+				}
+
+				//背景色
+				if cellData.Background != "" {
+					styleStr += `background-color: #F0F2F5;`
+				}
+
+				//将单元格设置为宽度百分比
+				if cellData.WidthPercent > 0 {
+					widthDecimal := decimal.NewFromFloat(cellData.WidthPercent)
+					cellOtherStr += ` width="` + widthDecimal.String() + `%" `
+				}
+
+				//文字排版(居中、左、右)
+				if cellData.TextAlign != "" {
+					cellOtherStr += ` align="` + cellData.TextAlign + `" `
+				}
+
+				//cell.Properties().SetAli
+				//设置是否加粗
+				if cellData.IsBold {
+					styleStr += `font-weight:bold;`
+				}
+
+				//设置字体大小
+				fontSize := 10.0
+				if cellData.FontSize > 0 {
+					fontSize = cellData.FontSize
+				}
+				fontDecimal := decimal.NewFromFloat(fontSize)
+				styleStr += `font-size: ` + fontDecimal.String() + `pt;`
+
+				bodyStr := cellData.Value
+
+				styleStr += `" `
+
+				cellStr += tdStr + styleStr + cellOtherStr + `>` + bodyStr + `</td>`
+			}
+		}
+		tableStr += cellStr + `</tr>`
+	}
+	tableStr += `</tbody></table>`
+
+	return
+}
+
+//根据html生成pdf
+func Html2Pdf(htmlStr, pdfPath string) (err error) {
+	pdfg, err := wkhtml.NewPDFGenerator()
+	if err != nil {
+		fmt.Println("err:", err)
+		return
+	}
+
+	//通过html生成page
+	page := wkhtml.NewPageReader(strings.NewReader(htmlStr))
+
+	//页眉设置
+	//page.HeaderLeft.Set("弘则弥道(上海)投资咨询有限公司")
+	//page.HeaderFontName.Set("宋体")
+	//page.HeaderFontSize.Set(8)
+	//page.HeaderSpacing.Set(4)
+	//page.HeaderLine.Set(true)
+
+	//page.HeaderSpacing.Set(10)
+
+	//页脚设置
+	page.FooterFontSize.Set(8)
+	page.FooterRight.Set("[page]")
+	page.FooterSpacing.Set(4)
+	//page.FooterLine.Set(true)
+	//page.EnableForms.Set(false)
+
+	//转换HTML表单为PDF表单
+	//page.EnableForms.Set(true)
+	//使用打印媒体类型而不是屏幕
+	//page.PrintMediaType.Set(true)
+
+	//允许从标题链接到目录
+	page.EnableTocBackLinks.Set(true)
+	//将该page插入到pdf中
+	pdfg.AddPage(page)
+
+	//pdfg.PageSize.Set(wkhtml.PageSizeA4)
+	//pdfg.MarginTop.Set(20)
+	//pdfg.MarginBottom.Set(5)
+	//pdfg.MarginLeft.Set(0)
+	//pdfg.
+
+	err = pdfg.Create()
+	if err != nil {
+		return
+	}
+
+	err = pdfg.WriteFile(pdfPath)
+	return
+}
+
+//获取表格列数据
+func getColList(item *contract_service_detail.ContractServiceDetail) (cellList []string, err error) {
+	cellList = make([]string, 0)
+	var serviceDetailReq contractReq.AddContractServiceDetailReq
+
+	tmpItem := *item
+	t := reflect.TypeOf(tmpItem)
+	v := reflect.ValueOf(tmpItem)
+	for k := 0; k < t.NumField(); k++ {
+		//获取结构体的参数名
+		tmpName := t.Field(k).Name
+		if strings.Contains(tmpName, "Col") {
+			//获取结构体该参数名的值
+			tmpValue := v.Field(k).String()
+			//如果值不为空的话,那么做下json转换
+			if tmpValue != "" {
+				err = json.Unmarshal([]byte(tmpValue), &serviceDetailReq)
+				if err != nil {
+					return
+				} else {
+					cellList = append(cellList, serviceDetailReq.Value)
+				}
+			}
+		}
+	}
+	return
+}

+ 42 - 0
utils/common.go

@@ -550,3 +550,45 @@ func ConvertToFormatDay(excelDaysString string) string {
 	resultTime := time.Unix(int64(baseOriginSecond+realDiffSecond), 0).Format("2006-01-02")
 	return resultTime
 }
+
+//人民币小写转大写
+func ConvertNumToCny(num float64) (str string, err error) {
+	strNum := strconv.FormatFloat(num*100, 'f', 0, 64)
+	sliceUnit := []string{"仟", "佰", "拾", "亿", "仟", "佰", "拾", "万", "仟", "佰", "拾", "元", "角", "分"}
+	// log.Println(sliceUnit[:len(sliceUnit)-2])
+	s := sliceUnit[len(sliceUnit)-len(strNum):]
+	upperDigitUnit := map[string]string{"0": "零", "1": "壹", "2": "贰", "3": "叁", "4": "肆", "5": "伍", "6": "陆", "7": "柒", "8": "捌", "9": "玖"}
+	for k, v := range strNum[:] {
+		str = str + upperDigitUnit[string(v)] + s[k]
+	}
+	reg, err := regexp.Compile(`零角零分$`)
+	str = reg.ReplaceAllString(str, "整")
+
+	reg, err = regexp.Compile(`零角`)
+	str = reg.ReplaceAllString(str, "零")
+
+	reg, err = regexp.Compile(`零分$`)
+	str = reg.ReplaceAllString(str, "整")
+
+	reg, err = regexp.Compile(`零[仟佰拾]`)
+	str = reg.ReplaceAllString(str, "零")
+
+	reg, err = regexp.Compile(`零{2,}`)
+	str = reg.ReplaceAllString(str, "零")
+
+	reg, err = regexp.Compile(`零亿`)
+	str = reg.ReplaceAllString(str, "亿")
+
+	reg, err = regexp.Compile(`零万`)
+	str = reg.ReplaceAllString(str, "万")
+
+	reg, err = regexp.Compile(`零*元`)
+	str = reg.ReplaceAllString(str, "元")
+
+	reg, err = regexp.Compile(`亿零{0, 3}万`)
+	str = reg.ReplaceAllString(str, "^元")
+
+	reg, err = regexp.Compile(`零元`)
+	str = reg.ReplaceAllString(str, "零")
+	return
+}

+ 23 - 0
utils/config.go

@@ -29,6 +29,20 @@ var (
 	WxPlatform2  int //用户来源,需要入库,用来保存该用户来自哪个平台,默认是:1
 )
 
+var (
+	STATIC_DIR string
+)
+
+var (
+	Bucketname       string = "hongze"
+	Endpoint         string
+	Imghost          string = "https://hongze.oss-accelerate.aliyuncs.com/"
+	Upload_dir       string = "static/images/"
+	Upload_Audio_Dir string = "static/audio/"
+	AccessKeyId      string = "LTAIFMZYQhS2BTvW"
+	AccessKeySecret  string = "12kk1ptCHoGWedhBnKRVW5hRJzq9Fq"
+)
+
 func init() {
 	RunMode = beego.AppConfig.String("run_mode")
 	fmt.Println(RunMode, "模式")
@@ -54,6 +68,11 @@ func init() {
 		TemplateRedirectUrl = "https://ficc.hzinsights.com/reportdtl?id="
 		WxPlatform = 1
 		TemplateIdByCompanyApply = "ZKcOfNIWBpwHJxpptufHIK1mp2nIwkT3cxub-35cFqI"
+
+		//oss
+		STATIC_DIR = "/home/static/imgs/"
+		Endpoint = "oss-cn-shanghai-internal.aliyuncs.com"
+
 	} else {
 		WxAppId = "wx9b5d7291e581233a"
 		WxAppSecret = "f4d52e34021eee262dce9682b31f8861"
@@ -62,6 +81,10 @@ func init() {
 		TemplateRedirectUrl = "http://rddpweb.brilliantstart.cn/reportdtl?id="
 		WxPlatform = 1
 		TemplateIdByCompanyApply = "eTalI1LbiT_B0mTaBeDTlfSMITenK8dQIgEB5yqjjvA"
+
+		//oss配置
+		STATIC_DIR = "static/imgs/"
+		Endpoint = "oss-cn-shanghai.aliyuncs.com"
 	}
 }
 

+ 2 - 1
utils/constants.go

@@ -69,8 +69,9 @@ const (
 	ROLE_TYPE_CODE_RAI_DEPARTMENT  = "rai_department"  //权益部门经理
 	ROLE_TYPE_CODE_FICC_RESEARCHR  = "ficc_researcher" //ficc研究员
 	ROLE_TYPE_CODE_RAI_RESEARCHR   = "rai_researcher"  //权益研究员
+	ROLE_TYPE_CODE_COMPLIANCE      = "compliance"      //合规角色
 
-	ROLE_TYPE_SELLERS = "'ficc_admin','ficc_seller','rai_admin','rai_seller','ficc_group','rai_group','ficc_department','rai_department'"
+	ROLE_TYPE_SELLERS = "'ficc_admin','ficc_seller','rai_admin','rai_seller','ficc_group','rai_group','ficc_department','rai_department','compliance'"
 )
 
 //客户类型