|
@@ -0,0 +1,454 @@
|
|
|
+package order
|
|
|
+
|
|
|
+import (
|
|
|
+ "app.yhyue.com/moapp/jybase/common"
|
|
|
+ "app.yhyue.com/moapp/jybase/date"
|
|
|
+ "app.yhyue.com/moapp/jybase/redis"
|
|
|
+ "context"
|
|
|
+ "crypto/md5"
|
|
|
+ "encoding/hex"
|
|
|
+ "fmt"
|
|
|
+ "github.com/gogf/gf/v2/errors/gerror"
|
|
|
+ "github.com/gogf/gf/v2/frame/g"
|
|
|
+ "github.com/gogf/gf/v2/util/gconv"
|
|
|
+ "github.com/lukasjarosch/go-docx"
|
|
|
+ "github.com/pkg/errors"
|
|
|
+ cncap "github.com/rosbit/cn-capitalizer"
|
|
|
+ "jyOrderManager/internal/jyutil"
|
|
|
+ "jyOrderManager/internal/model"
|
|
|
+ "log"
|
|
|
+ "math/rand"
|
|
|
+ "os/exec"
|
|
|
+ "path/filepath"
|
|
|
+ "runtime"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ bigMemberContractNoFlag = "JYDZ05"
|
|
|
+ subVipContractNoFlag = "JYDZ06"
|
|
|
+
|
|
|
+ /*
|
|
|
+ 1 超级订阅:https://www.kdocs.cn/l/cpqTn293crus
|
|
|
+ 2 大会员(购买主体“企业”,非单省版):https://www.kdocs.cn/l/cv9HKtmoO3tj
|
|
|
+ 3 大会员(购买主体“企业”,单省版):https://www.kdocs.cn/l/cm6OWJbTgmhE
|
|
|
+ 4 大会员(购买主体“个人”,非单省版-有子账号):https://www.kdocs.cn/l/cgtyiJGraYmd
|
|
|
+ 5 大会员(购买主体“个人”,非单省版-无子账号):https://www.kdocs.cn/l/clxt7vdFZ5Ea
|
|
|
+ 6 大会员(购买主体“个人”,单省版-无子账号):https://www.kdocs.cn/l/cscOCLIvvG1a;
|
|
|
+ 7 大会员(购买主体“个人”,单省版-有子账号):https://www.kdocs.cn/l/cfPHDdQsROBh
|
|
|
+ */
|
|
|
+ ContractTemplate_SUBVIP = "剑鱼标讯超级订阅产品服务协议书"
|
|
|
+ ContractTemplate_MEMBER_ENT_NOTSINGLE = "剑鱼标讯大会员产品服务协议书-(企业-非单省版)"
|
|
|
+ ContractTemplate_MEMBER_ENT_SINGLE = "剑鱼标讯大会员产品服务协议书-(企业-单省版)"
|
|
|
+ ContractTemplate_MEMBER_PERSON_NOTSINGLE_ACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-非单省版-有子账号)"
|
|
|
+ ContractTemplate_MEMBER_PERSON_NOTSINGLE_NOTACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-非单省版-无子账号)"
|
|
|
+ ContractTemplate_MEMBER_PERSON_SINGLE_NOTACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-单省版-无子账号)"
|
|
|
+ ContractTemplate_MEMBER_PERSON_SINGLE_ACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-单省版-有子账号)"
|
|
|
+)
|
|
|
+
|
|
|
+func GetContractPdf(ctx context.Context, orderCode, userName string) (interface{}, error) {
|
|
|
+ detail, orderQueryErr := func() (map[string]interface{}, error) {
|
|
|
+ if num, _ := g.DB().GetCount(ctx, "SELECT count(*) FROM jy_order_detail WHERE order_code=?", orderCode); num != 1 {
|
|
|
+ return nil, fmt.Errorf("当前订单不支持合同下载")
|
|
|
+ }
|
|
|
+ orderRes, err := g.DB().GetOne(ctx, "SELECT c.filter,c.service_type,a.audit_status,c.product_type,a.vip_type,a.salesperson,a.buy_subject,a.signing_subject,b.contract_status,b.seal_type,b.partyA_type,b.partyA_name,b.partyA_person,b.partyA_tel,b.partyA_address,b.partyB_person,b.remark,b.contract_money FROM dataexport_order a INNER JOIN jy_order_detail c ON a.order_code=c.order_code INNER JOIN contract b ON a.order_code=b.order_code WHERE a.order_code=? ", orderCode)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if orderRes.IsEmpty() {
|
|
|
+ return nil, fmt.Errorf("未找到订单")
|
|
|
+ }
|
|
|
+ return orderRes.Map(), nil
|
|
|
+ }()
|
|
|
+
|
|
|
+ if orderQueryErr != nil {
|
|
|
+ return nil, gerror.Wrapf(orderQueryErr, "GetContractPdf orderQueryErr %s订单查询异常 %v\n", orderCode, orderQueryErr)
|
|
|
+ }
|
|
|
+
|
|
|
+ filterMap := *common.ObjToMap(detail["filter"])
|
|
|
+ contractPass := func() error {
|
|
|
+ /*
|
|
|
+ 判断是否符合合同
|
|
|
+ (1)订单审核状态是“已通过”;
|
|
|
+ (2)签约主体为“北京剑鱼信息技术有限公司”,注:如签约主体为拓普则线下生成合同;
|
|
|
+ (3)协议状态为“签协议”(“已签协议”文案修改为“签协议”,涉及:创建订单、订单审核、订单详情)
|
|
|
+ (4)产品类型是“超级订阅”(且付费类型为“购买”、“续费”),或产品类型是“大会员”且会员套餐为“商机版2.0”、“专家版2.0”(且服务类型为“新购服务”、“延长服务”),注:超级订阅/大会员升级和其他产品类型,线下生成合同。
|
|
|
+ */
|
|
|
+ if common.IntAll(detail["audit_status"]) != 3 {
|
|
|
+ return fmt.Errorf("订单未审核通过")
|
|
|
+ }
|
|
|
+ if common.ObjToString(detail["signing_subject"]) != "h01" {
|
|
|
+ return fmt.Errorf("当前签约主体不支持生成电子合同")
|
|
|
+ }
|
|
|
+ if common.IntAll(detail["contract_status"]) != 1 {
|
|
|
+ return fmt.Errorf("协议状态不需要")
|
|
|
+ }
|
|
|
+ var service_type = gconv.Int(detail["service_type"])
|
|
|
+ switch common.ObjToString(detail["product_type"]) {
|
|
|
+ case "VIP订阅":
|
|
|
+ // 'vip订阅-0:购买 1:续费 2:升级',3试用
|
|
|
+ if !(service_type == 1 || service_type == 4) {
|
|
|
+ return fmt.Errorf("超级订阅当前订单不支持生成电子合同")
|
|
|
+ }
|
|
|
+ case "大会员":
|
|
|
+ level := common.IntAll(filterMap["comboId"])
|
|
|
+ if !(service_type == 1 || service_type == 4) && (level == 6 || level == 7) {
|
|
|
+ return fmt.Errorf("大会员当前订单不支持生成电子合同")
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return fmt.Errorf("当前订单类型不支持生成电子合同")
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }()
|
|
|
+
|
|
|
+ if contractPass != nil {
|
|
|
+ log.Printf("GetContractPdf contractPass %s合同校验异常 %v\n", orderCode, contractPass)
|
|
|
+ return nil, contractPass
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ contractDetail, err := func() (*ContractDetail, error) {
|
|
|
+ remark := common.ObjToString(detail["remark"])
|
|
|
+ ctd := &ContractDetail{
|
|
|
+ ContractFromInfo: &ContractFromInfo{
|
|
|
+ SealType: common.IntAll(detail["seal_type"]),
|
|
|
+ PartyAType: common.IntAll(detail["partyA_type"]),
|
|
|
+ PartyAName: common.ObjToString(detail["partyA_name"]),
|
|
|
+ PartyAPerson: common.ObjToString(detail["partyA_person"]),
|
|
|
+ PartyAContact: common.ObjToString(detail["partyA_tel"]),
|
|
|
+ PartyAAddress: common.ObjToString(detail["partyA_address"]),
|
|
|
+ PartyBPerson: common.ObjToString(detail["partyB_person"]),
|
|
|
+ Remark: common.ObjToString(common.If(remark != "", remark, "无")),
|
|
|
+ },
|
|
|
+ Amount: common.Float64All(detail["contract_money"]),
|
|
|
+ }
|
|
|
+ buy_subject := common.IntAll(detail["buy_subject"])
|
|
|
+
|
|
|
+ switch common.ObjToString(detail["product_type"]) {
|
|
|
+ case "VIP订阅":
|
|
|
+ var filter model.VipCycleFilter
|
|
|
+ if err := gconv.Struct(filterMap, &filter); err != nil {
|
|
|
+ return nil, gerror.Wrapf(err, "格式化超级订阅内容异常")
|
|
|
+ }
|
|
|
+ //时间
|
|
|
+ switch filter.BuyType { //1天 2月 3年 4季度
|
|
|
+ case 1:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("%d天(自然日)", filter.BuyCycle)
|
|
|
+ case 2:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("%d个月", filter.BuyCycle)
|
|
|
+ case 3:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("%d个月", filter.BuyCycle*12)
|
|
|
+ case 4:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("%d个月", filter.BuyCycle*3)
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("未知周期单位")
|
|
|
+ }
|
|
|
+ switch filter.GiftType { //1天 2月 3年 4季度
|
|
|
+ case 1:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("送%d天(自然日)", filter.GiftCycle)
|
|
|
+ case 2:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("送%d个月", filter.GiftCycle)
|
|
|
+ case 3:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("送%d个月", filter.GiftCycle*12)
|
|
|
+ case 4:
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("送%d个月", filter.GiftCycle*3)
|
|
|
+ }
|
|
|
+
|
|
|
+ if num := filter.BuyAccountCount; num > 0 {
|
|
|
+ ctd.Service = fmt.Sprintf("省份版超级订阅(%d个省)", num)
|
|
|
+ } else {
|
|
|
+ ctd.Service = "全国版超级订阅"
|
|
|
+ }
|
|
|
+ if buy_subject == 1 {
|
|
|
+ ctd.AccountNum = 1
|
|
|
+ } else {
|
|
|
+ ctd.AccountNum = 1 + filter.BuyAccountCount + filter.GiftAccountCount
|
|
|
+ }
|
|
|
+ ctd.ContractFlag = subVipContractNoFlag
|
|
|
+ ctd.ContractTemplate = ContractTemplate_SUBVIP
|
|
|
+ case "大会员":
|
|
|
+ level := common.IntAll(filterMap["comboId"])
|
|
|
+ isSINGLE := common.IntAll(filterMap["areaCount"]) == 1 //是单省版
|
|
|
+ hasACCOUNT := common.IntAll(filterMap["free_sub_num"])+common.IntAll(filterMap["pay_sub_num"]) >= 1 //有子账号
|
|
|
+ if buy_subject == 2 {
|
|
|
+ ctd.AccountNum = common.IntAll(detail["buy_count"])
|
|
|
+ }
|
|
|
+ if level == 6 {
|
|
|
+ if isSINGLE {
|
|
|
+ ctd.Service = "大会员商机版2.0(单省版)"
|
|
|
+ } else {
|
|
|
+ ctd.Service = "大会员商机版2.0"
|
|
|
+ }
|
|
|
+ } else if level == 7 {
|
|
|
+ ctd.Service = "大会员专家版2.0"
|
|
|
+ }
|
|
|
+
|
|
|
+ cycleType := common.IntAll(filterMap["cycleType"])
|
|
|
+ if cycleType == 1 {
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("%d天(自然日)", common.IntAll(filterMap["cycle"]))
|
|
|
+ } else {
|
|
|
+ ctd.ServiceTime = fmt.Sprintf("%d个月", common.IntAll(filterMap["cycle"]))
|
|
|
+ }
|
|
|
+
|
|
|
+ if buy_subject == 1 { //个人
|
|
|
+ ctd.AccountNum = common.IntAll(filterMap["free_sub_num"]) + common.IntAll(filterMap["pay_sub_num"]) + 1
|
|
|
+ if isSINGLE {
|
|
|
+ if hasACCOUNT {
|
|
|
+ ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_SINGLE_ACCOUNT
|
|
|
+ } else {
|
|
|
+ ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_SINGLE_NOTACCOUNT
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if hasACCOUNT {
|
|
|
+ ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_NOTSINGLE_ACCOUNT
|
|
|
+ } else {
|
|
|
+ ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_NOTSINGLE_NOTACCOUNT
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else { //企业
|
|
|
+ if isSINGLE {
|
|
|
+ ctd.ContractTemplate = ContractTemplate_MEMBER_ENT_SINGLE
|
|
|
+ } else {
|
|
|
+ ctd.ContractTemplate = ContractTemplate_MEMBER_ENT_NOTSINGLE
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctd.ContractFlag = bigMemberContractNoFlag
|
|
|
+ }
|
|
|
+ return ctd, nil
|
|
|
+ }()
|
|
|
+ if err != nil {
|
|
|
+ return nil, gerror.Wrapf(err, "获取合同内容出错")
|
|
|
+ }
|
|
|
+ filePath, getFilePathErr := func() (string, error) {
|
|
|
+ md5Str := contractDetail.GetMd5()
|
|
|
+ contract, err := g.DB().GetOne(ctx, "SELECT file_path FROM contract_pdf WHERE order_code=? and md5=?", orderCode, md5Str)
|
|
|
+ if err != nil {
|
|
|
+ return "", gerror.Wrapf(err, "获取pdf电子合同异常")
|
|
|
+ }
|
|
|
+ if contract.IsEmpty() {
|
|
|
+ now := time.Now()
|
|
|
+ docxAbsolutePath, err := contractDetail.getDocxFile(now)
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.Wrap(err, "生成电子合同docx文件异常")
|
|
|
+ }
|
|
|
+ if err = contractDetail.convertWordToPDF(docxAbsolutePath); err != nil {
|
|
|
+ return "", errors.Wrap(err, "生成电子合同pdf文件异常")
|
|
|
+ }
|
|
|
+ // 获取访问路径
|
|
|
+ var (
|
|
|
+ pdfFilePath = jyutil.GetRequestPath(strings.Replace(docxAbsolutePath, ".docx", ".pdf", 1))
|
|
|
+ contractCode = contractDetail.GetContractNoStr(now)
|
|
|
+ )
|
|
|
+ if _, err := g.DB().Save(ctx, "contract_pdf", g.Map{
|
|
|
+ "order_code": orderCode,
|
|
|
+ "code": contractCode,
|
|
|
+ "md5": md5Str,
|
|
|
+ "create_person": userName,
|
|
|
+ "file_path": pdfFilePath,
|
|
|
+ "create_time": now.Format(date.Date_Full_Layout),
|
|
|
+ }); err != nil {
|
|
|
+ return "", errors.Wrapf(err, "保存pdf内容异常")
|
|
|
+ }
|
|
|
+ if _, err := g.DB().Update(ctx, "contract", g.Map{
|
|
|
+ "contract_code": contractCode,
|
|
|
+ }, "order_code=?", orderCode); err != nil {
|
|
|
+ return "", errors.Wrapf(err, "更新合同异常")
|
|
|
+ }
|
|
|
+ return pdfFilePath, nil
|
|
|
+ }
|
|
|
+ return gconv.String(contract.Map()["file_path"]), nil
|
|
|
+ }()
|
|
|
+ if getFilePathErr != nil {
|
|
|
+ log.Printf("GetContractPdf getFilePathErr %s合同生成异常 %v\n", orderCode, getFilePathErr)
|
|
|
+ return nil, getFilePathErr
|
|
|
+ }
|
|
|
+ return filePath, nil
|
|
|
+}
|
|
|
+
|
|
|
+type ContractFromInfo struct {
|
|
|
+ SealType int `json:"seal_type" doc:"协议类型 1:有电子章 2:无电子章"`
|
|
|
+ PartyAType int `json:"partyAType" doc:"甲方类型 1:个人,2:企业"`
|
|
|
+ PartyAName string `json:"partyAName" doc:"甲方名称"`
|
|
|
+ PartyAPerson string `json:"partyAPerson" doc:"甲方联系人"`
|
|
|
+ PartyAContact string `json:"partyAContact" doc:"甲方联系方式"`
|
|
|
+ PartyAAddress string `json:"partyAAddresst" doc:"甲方联系地址"`
|
|
|
+ PartyBPerson string `json:"partyBPerson" doc:"乙方联系人"`
|
|
|
+ Remark string `json:"remark" doc:"协议备注"`
|
|
|
+}
|
|
|
+
|
|
|
+type ContractDetail struct {
|
|
|
+ *ContractFromInfo
|
|
|
+ Service string `json:"service" doc:"服务内容"`
|
|
|
+ AccountNum int `json:"accountNum" doc:"账户个数"`
|
|
|
+ ServiceTime string `json:"serviceTime" doc:"时长"`
|
|
|
+ Amount float64 `json:"amount" doc:"金额"`
|
|
|
+ ContractNo string `json:"contractNo" doc:"协议编号"`
|
|
|
+ ContractFlag string `json:"contractFlag" doc:"协议编号标识"`
|
|
|
+ ContractTemplate string `json:"contractTemplate" doc:"模版名字"`
|
|
|
+}
|
|
|
+
|
|
|
+// GetMd5 计算合同md5 (甲方+甲方类型+联系人+联系方式+地址+备注+乙方联系人+是否有电子章+服务内容+金额+账户个数+时长)
|
|
|
+func (detail *ContractDetail) GetMd5() string {
|
|
|
+ hasher := md5.New()
|
|
|
+ hasher.Write([]byte(fmt.Sprintf("%s_%d_%s_%s_%s_%s_%s_%d_%s_%d_%d_%s", detail.PartyAName, detail.PartyAType, detail.PartyAPerson, detail.PartyAContact,
|
|
|
+ detail.PartyAAddress, detail.Remark, detail.PartyBPerson, detail.SealType, detail.Service, detail.Amount, detail.AccountNum, detail.ServiceTime)))
|
|
|
+ hashInBytes := hasher.Sum(nil)
|
|
|
+ return hex.EncodeToString(hashInBytes)
|
|
|
+}
|
|
|
+
|
|
|
+func (detail *ContractDetail) getDocxFile(createTime time.Time) (filePath string, err error) {
|
|
|
+ replaceMap := docx.PlaceholderMap{
|
|
|
+ "PartyA.Name": detail.PartyAName, //甲方名字 {PartyA.Name}
|
|
|
+ "PartyA.Person": detail.PartyAPerson, //甲方联系人 {PartyA.Person}
|
|
|
+ "PartyA.Tel": detail.PartyAContact, //甲方联系方式 {PartyA.Tel}
|
|
|
+ "PartyA.Addr": detail.PartyAAddress, //甲方地址 {PartyA.Addr}
|
|
|
+ "Contract.No": detail.GetContractNoStr(createTime), //合同编号 {Contract.No}
|
|
|
+ "Contract.Service.Detail": detail.Service, //会员服务 {Contract.Service.Detail}
|
|
|
+ "Contract.Service.Time": detail.ServiceTime, //服务时长 {Contract.Service.Time}
|
|
|
+ "Contract.Account.Num": detail.AccountNum, //账号个数 {Contract.Account.Num}
|
|
|
+ "Contract.Remark": detail.Remark, //合同备注 {Contract.Remark}
|
|
|
+ "Contract.Amount": strconv.FormatFloat(detail.Amount/100, 'f', -1, 64), //合同金额 {Contract.Amount}
|
|
|
+ "Contract.Amount.Font": cncap.CapitalizeCurrency(detail.Amount / 100.0), //合同大写金额 {Contract.Amount.Font}
|
|
|
+ "PartyB.Person": detail.PartyBPerson, //乙方联系人 {PartyB.Person}
|
|
|
+ "PartyB.Date.Y": createTime.Year(), //乙方日期-年 {PartyB.Date.Year}
|
|
|
+ "PartyB.Date.M": fmt.Sprintf("%02d", int(createTime.Month())), //乙方日期-月 {PartyB.Date.Month}
|
|
|
+ "PartyB.Date.D": fmt.Sprintf("%02d", createTime.Day()), //乙方日期-日 {PartyB.Date.Day}
|
|
|
+ }
|
|
|
+ if detail.PartyAType == 2 {
|
|
|
+ replaceMap["PartyA.EndName"] = detail.PartyAName
|
|
|
+ } else {
|
|
|
+ replaceMap["PartyA.EndName"] = ""
|
|
|
+ }
|
|
|
+ numPageEmptyRows := 0
|
|
|
+ longNum := len([]rune(fmt.Sprintf("%s%s%d%s", detail.Service, detail.ServiceTime, detail.AccountNum, detail.Remark)))
|
|
|
+
|
|
|
+ if longNum <= 30 { //备注过短时,公章上移 18+13
|
|
|
+ numPageEmptyRows = 3
|
|
|
+ } else if longNum > 30 && longNum <= 88 {
|
|
|
+ numPageEmptyRows = 2
|
|
|
+ } else if longNum > 88 && longNum <= 146 {
|
|
|
+ numPageEmptyRows = 1
|
|
|
+ }
|
|
|
+ replaceMap["PageEmptyRows"] = strings.Repeat(" ", numPageEmptyRows*180)
|
|
|
+ //计算第一页空行数,把落款固定到最下部
|
|
|
+
|
|
|
+ // read and parse the template docx
|
|
|
+ doc, err := docx.Open(fmt.Sprintf("web/static/contract/%s.docx", detail.ContractTemplate))
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.Wrap(err, "docx文件模版文件异常")
|
|
|
+ }
|
|
|
+
|
|
|
+ // replace the keys with values from replaceMap
|
|
|
+ err = doc.ReplaceAll(replaceMap)
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.Wrap(err, "docx文件模版文件参数替换异常")
|
|
|
+ }
|
|
|
+ fileAbsolutePath := jyutil.GetFilePath("docx", "contract")
|
|
|
+
|
|
|
+ // write out a new file
|
|
|
+ err = doc.WriteToFile(fileAbsolutePath)
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.Wrap(err, "docx文件生成异常")
|
|
|
+ }
|
|
|
+ return fileAbsolutePath, nil
|
|
|
+}
|
|
|
+
|
|
|
+// GetContractNoStr 获取生成协议编号
|
|
|
+func (detail *ContractDetail) GetContractNoStr(createTime time.Time) string {
|
|
|
+ if detail.ContractNo != "" {
|
|
|
+ return detail.ContractNo
|
|
|
+ }
|
|
|
+ dataStr := createTime.Format(date.Date_yyyyMMdd)
|
|
|
+ key := fmt.Sprintf("%s_%s", detail.ContractFlag, dataStr)
|
|
|
+ detail.ContractNo = fmt.Sprintf("%s【%s】第%03d号", detail.ContractFlag, dataStr, redis.Incr("newother", key))
|
|
|
+ return detail.ContractNo
|
|
|
+}
|
|
|
+
|
|
|
+// convertWordToPDF word转pdf
|
|
|
+func (detail *ContractDetail) convertWordToPDF(inputDocxPath string) error {
|
|
|
+ var commandName string
|
|
|
+ var commandArg string
|
|
|
+
|
|
|
+ switch runtime.GOOS {
|
|
|
+ case "windows":
|
|
|
+ commandName = "cmd"
|
|
|
+ commandArg = "/c"
|
|
|
+ case "linux", "darwin":
|
|
|
+ commandName = "sh"
|
|
|
+ commandArg = "-c"
|
|
|
+ default:
|
|
|
+ return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
|
|
+ }
|
|
|
+
|
|
|
+ fileDir := filepath.Dir(inputDocxPath)
|
|
|
+ fileFullName := filepath.Base(inputDocxPath)
|
|
|
+ fileName := strings.TrimSuffix(fileFullName, filepath.Ext(fileFullName))
|
|
|
+ pdfFileFullPath := fmt.Sprintf("%s/%s.pdf", fileDir, fileName) //生成pdf路径
|
|
|
+ pngFileFullPath := fmt.Sprintf("%s/%s.png", fileDir, fileName) //生成图片路径
|
|
|
+ pngsMatchPath := fmt.Sprintf("%s/%s-*.png", fileDir, fileName) //生成图片路径
|
|
|
+
|
|
|
+ // 一、soffice 生成pdf文件
|
|
|
+ createPdfFile := exec.Command(commandName, commandArg, fmt.Sprintf("soffice --headless --invisible --convert-to pdf %s --outdir %s", inputDocxPath, fileDir))
|
|
|
+ if _, err := createPdfFile.CombinedOutput(); err != nil {
|
|
|
+ fmt.Printf(fmt.Sprintf("soffice --headless --invisible --convert-to pdf %s --outdir %s", inputDocxPath, fileDir))
|
|
|
+ return errors.Wrap(err, "Command execution createPdfFile failed:")
|
|
|
+ }
|
|
|
+ // 二、convert pdf内容转为图片
|
|
|
+ var hasSeal bool = detail.SealType == 1
|
|
|
+ if hasSeal {
|
|
|
+ createPngFile := exec.Command(commandName, commandArg, fmt.Sprintf("convert -density 300x300 -units pixelsperinch %s -background white -alpha remove -alpha off -quality 100 -antialias -resize 100%% %s ", pdfFileFullPath, pngFileFullPath))
|
|
|
+ if _, err := createPngFile.CombinedOutput(); err != nil {
|
|
|
+ fmt.Printf("convert -density 300x300 -units pixelsperinch %s -background white -alpha remove -alpha off -quality 100 -antialias -resize 100%% %s ", pdfFileFullPath, pngFileFullPath)
|
|
|
+ return errors.Wrap(err, "Command execution createPngFile failed:")
|
|
|
+ }
|
|
|
+ //每张图片
|
|
|
+ cmd := exec.Command("sh", "-c", fmt.Sprintf("ls %s", pngsMatchPath))
|
|
|
+ out, err := cmd.CombinedOutput()
|
|
|
+ if err != nil || len(out) == 0 {
|
|
|
+ return errors.Wrap(err, "Command execution ls pngs failed:")
|
|
|
+ }
|
|
|
+ for index, fileName := range strings.Split(string(out), "\n") {
|
|
|
+ if fileTmpPath := strings.TrimSpace(fileName); fileTmpPath != "" {
|
|
|
+ var x, y int64
|
|
|
+ rand.Seed(time.Now().UnixNano())
|
|
|
+ // 随机盖章位置 生成一个随机整数
|
|
|
+ if index == 0 { //首页需要盖着日期
|
|
|
+ x = 680 + rand.Int63n(10)
|
|
|
+ y = 130 + rand.Int63n(30)
|
|
|
+ //if len([]rune(fmt.Sprintf("%s%s%d%s", detail.Service, detail.ServiceTime, detail.AccountNum, detail.Remark))) <= 32 { //备注过短时,公章上移
|
|
|
+ // y = y + 50
|
|
|
+ //}
|
|
|
+ } else {
|
|
|
+ x = 300 + rand.Int63n(200)
|
|
|
+ y = 500 + rand.Int63n(200)
|
|
|
+ }
|
|
|
+
|
|
|
+ exeSeal := exec.Command(commandName, commandArg, fmt.Sprintf("convert %s \\( %s -resize 50%% \\) -gravity southeast -geometry +%d+%d -composite %s", fileTmpPath, "web/static/contract/z.png", x, y, fileTmpPath))
|
|
|
+ if _, err := exeSeal.CombinedOutput(); err != nil {
|
|
|
+ return errors.Wrap(err, "Command execution exeSeal failed:")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ createPngPdfFile := exec.Command(commandName, commandArg, fmt.Sprintf("convert %s %s", pngsMatchPath, pdfFileFullPath))
|
|
|
+ if _, err := createPngPdfFile.CombinedOutput(); err != nil {
|
|
|
+ fmt.Printf("convert %s %s", pngsMatchPath, pdfFileFullPath)
|
|
|
+ return errors.Wrap(err, "Command execution createPngFile failed:")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ createPngPdfFile := exec.Command(commandName, commandArg, fmt.Sprintf("convert -density 300x300 -units pixelsperinch %s -background white -alpha remove -alpha off -quality 100 -antialias -resize 100%% %s && convert %s %s", pdfFileFullPath, pngFileFullPath, pngsMatchPath, pdfFileFullPath))
|
|
|
+ if _, err := createPngPdfFile.CombinedOutput(); err != nil {
|
|
|
+ fmt.Println(fmt.Sprintf("convert -density 300x300 -units pixelsperinch %s -background white -alpha remove -alpha off -quality 100 -antialias -resize 100%% %s && convert %s %s", pdfFileFullPath, pngFileFullPath, pngsMatchPath, pdfFileFullPath))
|
|
|
+ return errors.Wrap(err, "Command execution createPngFile failed:")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 三、清除多余文件
|
|
|
+ clearFileCmd := exec.Command(commandName, commandArg, fmt.Sprintf("chmod -x %s && rm -rf %s %s", pngsMatchPath, pngsMatchPath, inputDocxPath))
|
|
|
+ if err := clearFileCmd.Run(); err != nil {
|
|
|
+ return errors.Wrap(err, "Command execution clearFileCmd failed:")
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|