contractPdf.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. package order
  2. import (
  3. "app.yhyue.com/moapp/jybase/common"
  4. "app.yhyue.com/moapp/jybase/date"
  5. "app.yhyue.com/moapp/jybase/redis"
  6. "context"
  7. "crypto/md5"
  8. "encoding/hex"
  9. "fmt"
  10. "github.com/gogf/gf/v2/errors/gerror"
  11. "github.com/gogf/gf/v2/frame/g"
  12. "github.com/gogf/gf/v2/util/gconv"
  13. "github.com/lukasjarosch/go-docx"
  14. "github.com/pkg/errors"
  15. cncap "github.com/rosbit/cn-capitalizer"
  16. "jyOrderManager/internal/jyutil"
  17. "jyOrderManager/internal/model"
  18. "log"
  19. "math/rand"
  20. "os/exec"
  21. "path/filepath"
  22. "runtime"
  23. "strconv"
  24. "strings"
  25. "time"
  26. )
  27. const (
  28. bigMemberContractNoFlag = "JYDZ05"
  29. subVipContractNoFlag = "JYDZ06"
  30. /*
  31. 1 超级订阅:https://www.kdocs.cn/l/cpqTn293crus
  32. 2 大会员(购买主体“企业”,非单省版):https://www.kdocs.cn/l/cv9HKtmoO3tj
  33. 3 大会员(购买主体“企业”,单省版):https://www.kdocs.cn/l/cm6OWJbTgmhE
  34. 4 大会员(购买主体“个人”,非单省版-有子账号):https://www.kdocs.cn/l/cgtyiJGraYmd
  35. 5 大会员(购买主体“个人”,非单省版-无子账号):https://www.kdocs.cn/l/clxt7vdFZ5Ea
  36. 6 大会员(购买主体“个人”,单省版-无子账号):https://www.kdocs.cn/l/cscOCLIvvG1a;
  37. 7 大会员(购买主体“个人”,单省版-有子账号):https://www.kdocs.cn/l/cfPHDdQsROBh
  38. */
  39. ContractTemplate_SUBVIP = "剑鱼标讯超级订阅产品服务协议书"
  40. ContractTemplate_MEMBER_ENT_NOTSINGLE = "剑鱼标讯大会员产品服务协议书-(企业-非单省版)"
  41. ContractTemplate_MEMBER_ENT_SINGLE = "剑鱼标讯大会员产品服务协议书-(企业-单省版)"
  42. ContractTemplate_MEMBER_PERSON_NOTSINGLE_ACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-非单省版-有子账号)"
  43. ContractTemplate_MEMBER_PERSON_NOTSINGLE_NOTACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-非单省版-无子账号)"
  44. ContractTemplate_MEMBER_PERSON_SINGLE_NOTACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-单省版-无子账号)"
  45. ContractTemplate_MEMBER_PERSON_SINGLE_ACCOUNT = "剑鱼标讯大会员产品服务协议书-(个人-单省版-有子账号)"
  46. )
  47. func GetContractPdf(ctx context.Context, orderCode, userName string) (interface{}, error) {
  48. detail, orderQueryErr := func() (map[string]interface{}, error) {
  49. if num, _ := g.DB().GetCount(ctx, "SELECT count(*) FROM jy_order_detail WHERE order_code=?", orderCode); num != 1 {
  50. return nil, fmt.Errorf("当前订单不支持合同下载")
  51. }
  52. 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)
  53. if err != nil {
  54. return nil, err
  55. }
  56. if orderRes.IsEmpty() {
  57. return nil, fmt.Errorf("未找到订单")
  58. }
  59. return orderRes.Map(), nil
  60. }()
  61. if orderQueryErr != nil {
  62. return nil, gerror.Wrapf(orderQueryErr, "GetContractPdf orderQueryErr %s订单查询异常 %v\n", orderCode, orderQueryErr)
  63. }
  64. filterMap := *common.ObjToMap(detail["filter"])
  65. contractPass := func() error {
  66. /*
  67. 判断是否符合合同
  68. (1)订单审核状态是“已通过”;
  69. (2)签约主体为“北京剑鱼信息技术有限公司”,注:如签约主体为拓普则线下生成合同;
  70. (3)协议状态为“签协议”(“已签协议”文案修改为“签协议”,涉及:创建订单、订单审核、订单详情)
  71. (4)产品类型是“超级订阅”(且付费类型为“购买”、“续费”),或产品类型是“大会员”且会员套餐为“商机版2.0”、“专家版2.0”(且服务类型为“新购服务”、“延长服务”),注:超级订阅/大会员升级和其他产品类型,线下生成合同。
  72. */
  73. if common.IntAll(detail["audit_status"]) != 3 {
  74. return fmt.Errorf("订单未审核通过")
  75. }
  76. if common.ObjToString(detail["signing_subject"]) != "h01" {
  77. return fmt.Errorf("当前签约主体不支持生成电子合同")
  78. }
  79. if common.IntAll(detail["contract_status"]) != 1 {
  80. return fmt.Errorf("协议状态不需要")
  81. }
  82. var service_type = gconv.Int(detail["service_type"])
  83. switch common.ObjToString(detail["product_type"]) {
  84. case "VIP订阅":
  85. // 'vip订阅-0:购买 1:续费 2:升级',3试用
  86. if !(service_type == 1 || service_type == 4) {
  87. return fmt.Errorf("超级订阅当前订单不支持生成电子合同")
  88. }
  89. case "大会员":
  90. level := common.IntAll(filterMap["comboId"])
  91. if !(service_type == 1 || service_type == 4) && (level == 6 || level == 7) {
  92. return fmt.Errorf("大会员当前订单不支持生成电子合同")
  93. }
  94. default:
  95. return fmt.Errorf("当前订单类型不支持生成电子合同")
  96. }
  97. return nil
  98. }()
  99. if contractPass != nil {
  100. log.Printf("GetContractPdf contractPass %s合同校验异常 %v\n", orderCode, contractPass)
  101. return nil, contractPass
  102. }
  103. //
  104. contractDetail, err := func() (*ContractDetail, error) {
  105. remark := common.ObjToString(detail["remark"])
  106. ctd := &ContractDetail{
  107. ContractFromInfo: &ContractFromInfo{
  108. SealType: common.IntAll(detail["seal_type"]),
  109. PartyAType: common.IntAll(detail["partyA_type"]),
  110. PartyAName: common.ObjToString(detail["partyA_name"]),
  111. PartyAPerson: common.ObjToString(detail["partyA_person"]),
  112. PartyAContact: common.ObjToString(detail["partyA_tel"]),
  113. PartyAAddress: common.ObjToString(detail["partyA_address"]),
  114. PartyBPerson: common.ObjToString(detail["partyB_person"]),
  115. Remark: common.ObjToString(common.If(remark != "", remark, "无")),
  116. },
  117. Amount: common.Float64All(detail["contract_money"]),
  118. }
  119. buy_subject := common.IntAll(detail["buy_subject"])
  120. switch common.ObjToString(detail["product_type"]) {
  121. case "VIP订阅":
  122. var filter model.VipCycleFilter
  123. if err := gconv.Struct(filterMap, &filter); err != nil {
  124. return nil, gerror.Wrapf(err, "格式化超级订阅内容异常")
  125. }
  126. //时间
  127. switch filter.BuyType { //1天 2月 3年 4季度
  128. case 1:
  129. ctd.ServiceTime = fmt.Sprintf("%d天(自然日)", filter.BuyCycle)
  130. case 2:
  131. ctd.ServiceTime = fmt.Sprintf("%d个月", filter.BuyCycle)
  132. case 3:
  133. ctd.ServiceTime = fmt.Sprintf("%d个月", filter.BuyCycle*12)
  134. case 4:
  135. ctd.ServiceTime = fmt.Sprintf("%d个月", filter.BuyCycle*3)
  136. default:
  137. return nil, fmt.Errorf("未知周期单位")
  138. }
  139. switch filter.GiftType { //1天 2月 3年 4季度
  140. case 1:
  141. ctd.ServiceTime = fmt.Sprintf("送%d天(自然日)", filter.GiftCycle)
  142. case 2:
  143. ctd.ServiceTime = fmt.Sprintf("送%d个月", filter.GiftCycle)
  144. case 3:
  145. ctd.ServiceTime = fmt.Sprintf("送%d个月", filter.GiftCycle*12)
  146. case 4:
  147. ctd.ServiceTime = fmt.Sprintf("送%d个月", filter.GiftCycle*3)
  148. }
  149. if num := filter.BuyAccountCount; num > 0 {
  150. ctd.Service = fmt.Sprintf("省份版超级订阅(%d个省)", num)
  151. } else {
  152. ctd.Service = "全国版超级订阅"
  153. }
  154. if buy_subject == 1 {
  155. ctd.AccountNum = 1
  156. } else {
  157. ctd.AccountNum = 1 + filter.BuyAccountCount + filter.GiftAccountCount
  158. }
  159. ctd.ContractFlag = subVipContractNoFlag
  160. ctd.ContractTemplate = ContractTemplate_SUBVIP
  161. case "大会员":
  162. level := common.IntAll(filterMap["comboId"])
  163. isSINGLE := common.IntAll(filterMap["areaCount"]) == 1 //是单省版
  164. hasACCOUNT := common.IntAll(filterMap["free_sub_num"])+common.IntAll(filterMap["pay_sub_num"]) >= 1 //有子账号
  165. if buy_subject == 2 {
  166. ctd.AccountNum = common.IntAll(detail["buy_count"])
  167. }
  168. if level == 6 {
  169. if isSINGLE {
  170. ctd.Service = "大会员商机版2.0(单省版)"
  171. } else {
  172. ctd.Service = "大会员商机版2.0"
  173. }
  174. } else if level == 7 {
  175. ctd.Service = "大会员专家版2.0"
  176. }
  177. cycleType := common.IntAll(filterMap["cycleType"])
  178. if cycleType == 1 {
  179. ctd.ServiceTime = fmt.Sprintf("%d天(自然日)", common.IntAll(filterMap["cycle"]))
  180. } else {
  181. ctd.ServiceTime = fmt.Sprintf("%d个月", common.IntAll(filterMap["cycle"]))
  182. }
  183. if buy_subject == 1 { //个人
  184. ctd.AccountNum = common.IntAll(filterMap["free_sub_num"]) + common.IntAll(filterMap["pay_sub_num"]) + 1
  185. if isSINGLE {
  186. if hasACCOUNT {
  187. ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_SINGLE_ACCOUNT
  188. } else {
  189. ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_SINGLE_NOTACCOUNT
  190. }
  191. } else {
  192. if hasACCOUNT {
  193. ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_NOTSINGLE_ACCOUNT
  194. } else {
  195. ctd.ContractTemplate = ContractTemplate_MEMBER_PERSON_NOTSINGLE_NOTACCOUNT
  196. }
  197. }
  198. } else { //企业
  199. if isSINGLE {
  200. ctd.ContractTemplate = ContractTemplate_MEMBER_ENT_SINGLE
  201. } else {
  202. ctd.ContractTemplate = ContractTemplate_MEMBER_ENT_NOTSINGLE
  203. }
  204. }
  205. ctd.ContractFlag = bigMemberContractNoFlag
  206. }
  207. return ctd, nil
  208. }()
  209. if err != nil {
  210. return nil, gerror.Wrapf(err, "获取合同内容出错")
  211. }
  212. filePath, getFilePathErr := func() (string, error) {
  213. md5Str := contractDetail.GetMd5()
  214. contract, err := g.DB().GetOne(ctx, "SELECT file_path FROM contract_pdf WHERE order_code=? and md5=?", orderCode, md5Str)
  215. if err != nil {
  216. return "", gerror.Wrapf(err, "获取pdf电子合同异常")
  217. }
  218. if contract.IsEmpty() {
  219. now := time.Now()
  220. docxAbsolutePath, err := contractDetail.getDocxFile(now)
  221. if err != nil {
  222. return "", errors.Wrap(err, "生成电子合同docx文件异常")
  223. }
  224. if err = contractDetail.convertWordToPDF(docxAbsolutePath); err != nil {
  225. return "", errors.Wrap(err, "生成电子合同pdf文件异常")
  226. }
  227. // 获取访问路径
  228. var (
  229. pdfFilePath = jyutil.GetRequestPath(strings.Replace(docxAbsolutePath, ".docx", ".pdf", 1))
  230. contractCode = contractDetail.GetContractNoStr(now)
  231. )
  232. if _, err := g.DB().Save(ctx, "contract_pdf", g.Map{
  233. "order_code": orderCode,
  234. "code": contractCode,
  235. "md5": md5Str,
  236. "create_person": userName,
  237. "file_path": pdfFilePath,
  238. "create_time": now.Format(date.Date_Full_Layout),
  239. }); err != nil {
  240. return "", errors.Wrapf(err, "保存pdf内容异常")
  241. }
  242. if _, err := g.DB().Update(ctx, "contract", g.Map{
  243. "contract_code": contractCode,
  244. }, "order_code=?", orderCode); err != nil {
  245. return "", errors.Wrapf(err, "更新合同异常")
  246. }
  247. return pdfFilePath, nil
  248. }
  249. return gconv.String(contract.Map()["file_path"]), nil
  250. }()
  251. if getFilePathErr != nil {
  252. log.Printf("GetContractPdf getFilePathErr %s合同生成异常 %v\n", orderCode, getFilePathErr)
  253. return nil, getFilePathErr
  254. }
  255. return filePath, nil
  256. }
  257. type ContractFromInfo struct {
  258. SealType int `json:"seal_type" doc:"协议类型 1:有电子章 2:无电子章"`
  259. PartyAType int `json:"partyAType" doc:"甲方类型 1:个人,2:企业"`
  260. PartyAName string `json:"partyAName" doc:"甲方名称"`
  261. PartyAPerson string `json:"partyAPerson" doc:"甲方联系人"`
  262. PartyAContact string `json:"partyAContact" doc:"甲方联系方式"`
  263. PartyAAddress string `json:"partyAAddresst" doc:"甲方联系地址"`
  264. PartyBPerson string `json:"partyBPerson" doc:"乙方联系人"`
  265. Remark string `json:"remark" doc:"协议备注"`
  266. }
  267. type ContractDetail struct {
  268. *ContractFromInfo
  269. Service string `json:"service" doc:"服务内容"`
  270. AccountNum int `json:"accountNum" doc:"账户个数"`
  271. ServiceTime string `json:"serviceTime" doc:"时长"`
  272. Amount float64 `json:"amount" doc:"金额"`
  273. ContractNo string `json:"contractNo" doc:"协议编号"`
  274. ContractFlag string `json:"contractFlag" doc:"协议编号标识"`
  275. ContractTemplate string `json:"contractTemplate" doc:"模版名字"`
  276. }
  277. // GetMd5 计算合同md5 (甲方+甲方类型+联系人+联系方式+地址+备注+乙方联系人+是否有电子章+服务内容+金额+账户个数+时长)
  278. func (detail *ContractDetail) GetMd5() string {
  279. hasher := md5.New()
  280. 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,
  281. detail.PartyAAddress, detail.Remark, detail.PartyBPerson, detail.SealType, detail.Service, detail.Amount, detail.AccountNum, detail.ServiceTime)))
  282. hashInBytes := hasher.Sum(nil)
  283. return hex.EncodeToString(hashInBytes)
  284. }
  285. func (detail *ContractDetail) getDocxFile(createTime time.Time) (filePath string, err error) {
  286. replaceMap := docx.PlaceholderMap{
  287. "PartyA.Name": detail.PartyAName, //甲方名字 {PartyA.Name}
  288. "PartyA.Person": detail.PartyAPerson, //甲方联系人 {PartyA.Person}
  289. "PartyA.Tel": detail.PartyAContact, //甲方联系方式 {PartyA.Tel}
  290. "PartyA.Addr": detail.PartyAAddress, //甲方地址 {PartyA.Addr}
  291. "Contract.No": detail.GetContractNoStr(createTime), //合同编号 {Contract.No}
  292. "Contract.Service.Detail": detail.Service, //会员服务 {Contract.Service.Detail}
  293. "Contract.Service.Time": detail.ServiceTime, //服务时长 {Contract.Service.Time}
  294. "Contract.Account.Num": detail.AccountNum, //账号个数 {Contract.Account.Num}
  295. "Contract.Remark": detail.Remark, //合同备注 {Contract.Remark}
  296. "Contract.Amount": strconv.FormatFloat(detail.Amount/100, 'f', -1, 64), //合同金额 {Contract.Amount}
  297. "Contract.Amount.Font": cncap.CapitalizeCurrency(detail.Amount / 100.0), //合同大写金额 {Contract.Amount.Font}
  298. "PartyB.Person": detail.PartyBPerson, //乙方联系人 {PartyB.Person}
  299. "PartyB.Date.Y": createTime.Year(), //乙方日期-年 {PartyB.Date.Year}
  300. "PartyB.Date.M": fmt.Sprintf("%02d", int(createTime.Month())), //乙方日期-月 {PartyB.Date.Month}
  301. "PartyB.Date.D": fmt.Sprintf("%02d", createTime.Day()), //乙方日期-日 {PartyB.Date.Day}
  302. }
  303. if detail.PartyAType == 2 {
  304. replaceMap["PartyA.EndName"] = detail.PartyAName
  305. } else {
  306. replaceMap["PartyA.EndName"] = ""
  307. }
  308. numPageEmptyRows := 0
  309. longNum := len([]rune(fmt.Sprintf("%s%s%d%s", detail.Service, detail.ServiceTime, detail.AccountNum, detail.Remark)))
  310. if longNum <= 30 { //备注过短时,公章上移 18+13
  311. numPageEmptyRows = 3
  312. } else if longNum > 30 && longNum <= 88 {
  313. numPageEmptyRows = 2
  314. } else if longNum > 88 && longNum <= 146 {
  315. numPageEmptyRows = 1
  316. }
  317. replaceMap["PageEmptyRows"] = strings.Repeat(" ", numPageEmptyRows*180)
  318. //计算第一页空行数,把落款固定到最下部
  319. // read and parse the template docx
  320. doc, err := docx.Open(fmt.Sprintf("web/static/contract/%s.docx", detail.ContractTemplate))
  321. if err != nil {
  322. return "", errors.Wrap(err, "docx文件模版文件异常")
  323. }
  324. // replace the keys with values from replaceMap
  325. err = doc.ReplaceAll(replaceMap)
  326. if err != nil {
  327. return "", errors.Wrap(err, "docx文件模版文件参数替换异常")
  328. }
  329. fileAbsolutePath := jyutil.GetFilePath("docx", "contract")
  330. // write out a new file
  331. err = doc.WriteToFile(fileAbsolutePath)
  332. if err != nil {
  333. return "", errors.Wrap(err, "docx文件生成异常")
  334. }
  335. return fileAbsolutePath, nil
  336. }
  337. // GetContractNoStr 获取生成协议编号
  338. func (detail *ContractDetail) GetContractNoStr(createTime time.Time) string {
  339. if detail.ContractNo != "" {
  340. return detail.ContractNo
  341. }
  342. dataStr := createTime.Format(date.Date_yyyyMMdd)
  343. key := fmt.Sprintf("%s_%s", detail.ContractFlag, dataStr)
  344. detail.ContractNo = fmt.Sprintf("%s【%s】第%03d号", detail.ContractFlag, dataStr, redis.Incr("newother", key))
  345. return detail.ContractNo
  346. }
  347. // convertWordToPDF word转pdf
  348. func (detail *ContractDetail) convertWordToPDF(inputDocxPath string) error {
  349. var commandName string
  350. var commandArg string
  351. switch runtime.GOOS {
  352. case "windows":
  353. commandName = "cmd"
  354. commandArg = "/c"
  355. case "linux", "darwin":
  356. commandName = "sh"
  357. commandArg = "-c"
  358. default:
  359. return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
  360. }
  361. fileDir := filepath.Dir(inputDocxPath)
  362. fileFullName := filepath.Base(inputDocxPath)
  363. fileName := strings.TrimSuffix(fileFullName, filepath.Ext(fileFullName))
  364. pdfFileFullPath := fmt.Sprintf("%s/%s.pdf", fileDir, fileName) //生成pdf路径
  365. pngFileFullPath := fmt.Sprintf("%s/%s.png", fileDir, fileName) //生成图片路径
  366. pngsMatchPath := fmt.Sprintf("%s/%s-*.png", fileDir, fileName) //生成图片路径
  367. // 一、soffice 生成pdf文件
  368. createPdfFile := exec.Command(commandName, commandArg, fmt.Sprintf("soffice --headless --invisible --convert-to pdf %s --outdir %s", inputDocxPath, fileDir))
  369. if _, err := createPdfFile.CombinedOutput(); err != nil {
  370. fmt.Printf(fmt.Sprintf("soffice --headless --invisible --convert-to pdf %s --outdir %s", inputDocxPath, fileDir))
  371. return errors.Wrap(err, "Command execution createPdfFile failed:")
  372. }
  373. // 二、convert pdf内容转为图片
  374. var hasSeal bool = detail.SealType == 1
  375. if hasSeal {
  376. 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))
  377. if _, err := createPngFile.CombinedOutput(); err != nil {
  378. fmt.Printf("convert -density 300x300 -units pixelsperinch %s -background white -alpha remove -alpha off -quality 100 -antialias -resize 100%% %s ", pdfFileFullPath, pngFileFullPath)
  379. return errors.Wrap(err, "Command execution createPngFile failed:")
  380. }
  381. //每张图片
  382. cmd := exec.Command("sh", "-c", fmt.Sprintf("ls %s", pngsMatchPath))
  383. out, err := cmd.CombinedOutput()
  384. if err != nil || len(out) == 0 {
  385. return errors.Wrap(err, "Command execution ls pngs failed:")
  386. }
  387. for index, fileName := range strings.Split(string(out), "\n") {
  388. if fileTmpPath := strings.TrimSpace(fileName); fileTmpPath != "" {
  389. var x, y int64
  390. rand.Seed(time.Now().UnixNano())
  391. // 随机盖章位置 生成一个随机整数
  392. if index == 0 { //首页需要盖着日期
  393. x = 680 + rand.Int63n(10)
  394. y = 130 + rand.Int63n(30)
  395. //if len([]rune(fmt.Sprintf("%s%s%d%s", detail.Service, detail.ServiceTime, detail.AccountNum, detail.Remark))) <= 32 { //备注过短时,公章上移
  396. // y = y + 50
  397. //}
  398. } else {
  399. x = 300 + rand.Int63n(200)
  400. y = 500 + rand.Int63n(200)
  401. }
  402. 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))
  403. if _, err := exeSeal.CombinedOutput(); err != nil {
  404. return errors.Wrap(err, "Command execution exeSeal failed:")
  405. }
  406. }
  407. }
  408. createPngPdfFile := exec.Command(commandName, commandArg, fmt.Sprintf("convert %s %s", pngsMatchPath, pdfFileFullPath))
  409. if _, err := createPngPdfFile.CombinedOutput(); err != nil {
  410. fmt.Printf("convert %s %s", pngsMatchPath, pdfFileFullPath)
  411. return errors.Wrap(err, "Command execution createPngFile failed:")
  412. }
  413. } else {
  414. 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))
  415. if _, err := createPngPdfFile.CombinedOutput(); err != nil {
  416. 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))
  417. return errors.Wrap(err, "Command execution createPngFile failed:")
  418. }
  419. }
  420. // 三、清除多余文件
  421. clearFileCmd := exec.Command(commandName, commandArg, fmt.Sprintf("chmod -x %s && rm -rf %s %s", pngsMatchPath, pngsMatchPath, inputDocxPath))
  422. if err := clearFileCmd.Run(); err != nil {
  423. return errors.Wrap(err, "Command execution clearFileCmd failed:")
  424. }
  425. return nil
  426. }