invoiceManager.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package service
  2. import (
  3. "ElectronicInvoice/internal/consts"
  4. "context"
  5. "github.com/gogf/gf/v2/database/gdb"
  6. "github.com/gogf/gf/v2/errors/gerror"
  7. "github.com/gogf/gf/v2/frame/g"
  8. "github.com/gogf/gf/v2/os/gcron"
  9. "github.com/gogf/gf/v2/util/gconv"
  10. "strconv"
  11. "time"
  12. )
  13. var (
  14. JyInvoiceManager *InvoiceManager
  15. )
  16. type InvoiceManager struct {
  17. Auth *TripartiteAuth
  18. jobRunning bool //开票是否运行中
  19. StopRunning bool //财务占用账号,需要暂停任务
  20. Login bool //登录状态
  21. OCRPass bool //活体检测状态是否通过
  22. runPool chan bool //任务(每次只能进行一个开票任务)
  23. phoneCode chan string //短信验证码池
  24. ScanLogin chan bool //扫码登录
  25. }
  26. func init() {
  27. JyInvoiceManager = createInvoiceManager()
  28. job, err := gcron.Add(context.Background(), g.Cfg().MustGet(context.Background(), "invoiceJob.cron").String(), JyInvoiceManager.RunJob, "invoiceJob")
  29. if err != nil {
  30. panic(err)
  31. }
  32. job.Start()
  33. //go JyInvoiceManager.RunOneJob(context.Background())//流程
  34. //go JyInvoiceManager.Demo(context.Background()) //开票
  35. //go JyInvoiceManager.RedDemo(context.Background())//红冲
  36. }
  37. func createInvoiceManager() *InvoiceManager {
  38. return &InvoiceManager{
  39. Auth: createTripartite(),
  40. Login: true, //默认已经登录
  41. OCRPass: true,
  42. runPool: make(chan bool, 1), //开票只能单线程跑
  43. phoneCode: make(chan string, 1), //手机验证码
  44. ScanLogin: make(chan bool, 1), //扫码登录通知
  45. }
  46. }
  47. func (im *InvoiceManager) ReleasePool() {
  48. <-im.runPool
  49. }
  50. func (im *InvoiceManager) MobileVerificationCode(code string) error {
  51. if code == "" {
  52. return gerror.New("验证码为空")
  53. }
  54. select {
  55. case <-time.After(time.Minute):
  56. return gerror.New("验证码接收超时")
  57. case im.phoneCode <- code:
  58. return nil
  59. }
  60. }
  61. func (im *InvoiceManager) MobileVerificationClear() error {
  62. select {
  63. case <-time.After(time.Minute):
  64. return gerror.New("清除验证码超时")
  65. case <-im.phoneCode:
  66. return nil
  67. }
  68. }
  69. // RunJob 开票定时任务
  70. func (im *InvoiceManager) RunJob(ctx context.Context) {
  71. if im.jobRunning || im.StopRunning {
  72. g.Log().Infof(ctx, "RunJob-程序本次任务中断 jobRunning:%v StopRunning:%v", im.jobRunning, im.StopRunning)
  73. return
  74. }
  75. im.jobRunning = true
  76. defer func() {
  77. im.jobRunning = false
  78. g.Log().Infof(ctx, "RunJob-开票任务完成")
  79. }()
  80. if g.Cfg().MustGet(ctx, "invoiceJob.stop", false).Bool() {
  81. g.Log().Infof(ctx, "RunJob-开票程序任务已暂停,开启请删除 config.json > invoiceJob.stop")
  82. return
  83. }
  84. if !im.Login {
  85. if err := im.Auth.Login(); err != nil {
  86. g.Log().Errorf(ctx, "模拟登录异常 %v", err)
  87. return
  88. } else {
  89. g.Log().Infof(ctx, "登录成功")
  90. }
  91. }
  92. //TODO 普通蓝票任务
  93. total, okNum, err := im.simpleMakeInvoice(ctx)
  94. if err != nil {
  95. if gerror.Is(err, consts.LoginOutErr) {
  96. g.Log().Infof(ctx, "RunJob-任务中止-身份过期,需要重新登录")
  97. return
  98. }
  99. g.Log().Errorf(ctx, "RunJob-开蓝票任务异常 %v", err)
  100. } else {
  101. g.Log().Infof(ctx, "RunJob-开蓝票任务完成 共%d个 完成%d个", total, okNum)
  102. }
  103. //TODO 红票任务
  104. }
  105. // simpleMakeInvoice 简单开票
  106. func (im *InvoiceManager) simpleMakeInvoice(ctx context.Context) (total, okNum int, err error) {
  107. var (
  108. res gdb.Result
  109. )
  110. //查询需要开票的数据
  111. res, err = g.DB().Query(ctx, "SELECT a.*,b.pay_way,b.order_money,b.pay_money FROM invoice a INNER JOIN dataexport_order b ON a.order_code=b.order_code WHERE a.invoice_status=0 AND a.invoice_changed=0 AND a.invoice_variety='普通发票(电子发票)' AND a.invoice_order_code is NULL")
  112. if err != nil {
  113. g.Log().Errorf(ctx, "RunJob-simpleMakeInvoice-查询待开票异常 %s", err)
  114. return -1, -1, gerror.Wrap(err, "simpleMakeInvoice-查询待开票异常")
  115. }
  116. g.Log().Infof(ctx, "RunJob-simpleMakeInvoice-本次共加载%d条开票记录", res.Len())
  117. total, okNum = res.Len(), 0
  118. for _, m := range res.List() {
  119. select {
  120. case im.runPool <- true:
  121. case <-time.After(time.Minute * 5):
  122. g.Log().Errorf(ctx, "RunJob-simpleMakeInvoice-开票等待超时,结束此次任务")
  123. return
  124. }
  125. var (
  126. orderCode = gconv.String(m["order_code"])
  127. phone = gconv.String(m["phone_num"])
  128. iType = gconv.String(m["invoice_type"])
  129. prices float64
  130. )
  131. //公对公转账 账单金额可以修改 开发票应取实付金额 pay_money
  132. //微信支付宝支付 pay_money为订单金额减去微信or支付包红包
  133. if gconv.String(m["pay_way"]) == "transferAccounts" {
  134. prices = gconv.Float64(m["pay_money"]) / float64(100)
  135. } else {
  136. prices = gconv.Float64(m["order_money"]) / float64(100)
  137. }
  138. c := MakeInvoiceData{
  139. Type: "2",
  140. Id: orderCode,
  141. Lxdh: phone,
  142. Fhr: g.Cfg().MustGet(ctx, "company.hfr", "贺鹏飞").String(),
  143. InvoiceArr: []MakeInvoiceItems{{
  144. Xmmc: g.Cfg().MustGet(ctx, "company.taxCode").String(), //开票项
  145. WhStatus: 1, //开票项是否维护
  146. Je: strconv.FormatFloat(prices, 'f', -1, 64), //金额
  147. Sl: "1", //数量
  148. }},
  149. }
  150. if iType == "单位" {
  151. c.Gmfmc = gconv.String(m["company_name"])
  152. c.Gmfnsrsbh = gconv.String(m["taxpayer_identnum"])
  153. } else {
  154. c.Gmfmc = iType
  155. }
  156. err = im.Auth.MakeSingleInvoice(c)
  157. if err != nil {
  158. im.ReleasePool()
  159. if gerror.Is(err, consts.LoginOutErr) {
  160. g.Log().Infof(ctx, "RunJob-simpleMakeInvoice-身份过期,需要重新登录")
  161. return
  162. }
  163. g.Log().Errorf(ctx, "RunJob-simpleMakeInvoice-开票接口调用异常 %v", err)
  164. continue
  165. }
  166. okNum++
  167. }
  168. return
  169. }