invoiceManager.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. runPool chan bool //任务(每次只能进行一个开票任务)
  22. phoneCode chan string //短信验证码池
  23. ScanLogin chan bool //扫码登录
  24. }
  25. func init() {
  26. JyInvoiceManager = createInvoiceManager()
  27. job, err := gcron.Add(context.Background(), g.Cfg().MustGet(context.Background(), "invoiceJob.cron").String(), JyInvoiceManager.RunJob, "invoiceJob")
  28. if err != nil {
  29. panic(err)
  30. }
  31. job.Start()
  32. JyInvoiceManager.Auth.CallBackInit()
  33. //go JyInvoiceManager.RunOneJob(context.Background())//流程
  34. //go JyInvoiceManager.Demo(context.Background()) //开票
  35. //go JyInvoiceManager.Auth.Login() //登录
  36. //go JyInvoiceManager.RedDemo(context.Background())//红冲
  37. //err = JyInvoiceManager.Auth.AddTaxCode([]Billitem{{
  38. // Name: "技术服务费",
  39. // Ssflbm: "3040205000000000000",
  40. // Thirdid: "6f2e6a7a-18a7-4f21-b52e-a47437f817cc",
  41. //}})
  42. //if err != nil {
  43. // panic(err)
  44. //}
  45. //list, err := JyInvoiceManager.Auth.GetSuccessTaxCodeList()
  46. //g.Dump(list, err)
  47. }
  48. func createInvoiceManager() *InvoiceManager {
  49. return &InvoiceManager{
  50. Auth: createTripartite(),
  51. Login: true, //默认已经登录
  52. runPool: make(chan bool, 1), //开票只能单线程跑
  53. phoneCode: make(chan string, 1), //手机验证码
  54. ScanLogin: make(chan bool, 1), //扫码登录通知
  55. }
  56. }
  57. func (im *InvoiceManager) ReleasePool() {
  58. <-im.runPool
  59. }
  60. func (im *InvoiceManager) MobileVerificationCode(code string) error {
  61. if code == "" {
  62. return gerror.New("验证码为空")
  63. }
  64. select {
  65. case <-time.After(time.Minute):
  66. return gerror.New("验证码接收超时")
  67. case im.phoneCode <- code:
  68. return nil
  69. }
  70. }
  71. func (im *InvoiceManager) MobileVerificationClear() error {
  72. select {
  73. case <-time.After(time.Minute):
  74. return gerror.New("清除验证码超时")
  75. case <-im.phoneCode:
  76. return nil
  77. }
  78. }
  79. // RunJob 开票定时任务
  80. func (im *InvoiceManager) RunJob(ctx context.Context) {
  81. if im.jobRunning || im.StopRunning {
  82. g.Log().Infof(ctx, "RunJob-程序本次任务中断 jobRunning:%v StopRunning:%v", im.jobRunning, im.StopRunning)
  83. return
  84. }
  85. im.jobRunning = true
  86. defer func() {
  87. im.jobRunning = false
  88. g.Log().Infof(ctx, "RunJob-开票任务完成")
  89. }()
  90. if g.Cfg().MustGet(ctx, "invoiceJob.stop", false).Bool() {
  91. g.Log().Infof(ctx, "RunJob-开票程序任务已暂停,开启请删除 config.json > invoiceJob.stop")
  92. return
  93. }
  94. if !im.Login {
  95. if err := im.Auth.Login(); err != nil {
  96. g.Log().Errorf(ctx, "模拟登录异常 %v", err)
  97. return
  98. } else {
  99. g.Log().Infof(ctx, "登录成功")
  100. }
  101. }
  102. //TODO 普通蓝票任务
  103. total, okNum, err := im.simpleMakeInvoice(ctx)
  104. if err != nil {
  105. if gerror.Is(err, consts.LoginOutErr) {
  106. g.Log().Infof(ctx, "RunJob-任务中止-开蓝票任务身份过期,需要重新登录")
  107. return
  108. } else if gerror.Is(err, consts.WaitTimeOut) {
  109. g.Log().Errorf(ctx, "RunJob-任务中止-开蓝票任务等待超时异常 %v", err)
  110. return
  111. }
  112. g.Log().Errorf(ctx, "RunJob-开蓝票任务异常 %v", err)
  113. } else {
  114. g.Log().Infof(ctx, "RunJob-开蓝票任务完成 共%d个 完成%d个", total, okNum)
  115. }
  116. // TODO 蓝票自助开票(管理后台扫码开票)
  117. total, okNum, err = im.selfMakeInvoice(ctx)
  118. if err != nil {
  119. if gerror.Is(err, consts.LoginOutErr) {
  120. g.Log().Infof(ctx, "RunJob-任务中止-开自助蓝票任务身份过期,需要重新登录")
  121. return
  122. } else if gerror.Is(err, consts.WaitTimeOut) {
  123. g.Log().Errorf(ctx, "RunJob-任务中止-开自助蓝票任务等待超时异常 %v", err)
  124. return
  125. }
  126. g.Log().Errorf(ctx, "RunJob-开自助蓝票任务异常 %v", err)
  127. } else {
  128. g.Log().Infof(ctx, "RunJob-开自助蓝票任务完成 共%d个 完成%d个", total, okNum)
  129. }
  130. //TODO 红票任务
  131. }
  132. // simpleMakeInvoice 简单开票
  133. func (im *InvoiceManager) simpleMakeInvoice(ctx context.Context) (total, okNum int, err error) {
  134. var (
  135. res gdb.Result
  136. )
  137. //查询需要开票的数据
  138. //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 AND a.create_time > ? ", consts.InvoiceStartTime.Unix())
  139. res, err = g.DB().Query(ctx, "SELECT a.*,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 like '%普通发票%' AND (a.invoice_order_code is NULL OR a.invoice_order_code not like '%,%') AND a.create_time > ? ", consts.InvoiceStartTime.Unix())
  140. if err != nil {
  141. return -1, -1, gerror.Wrap(err, "simpleMakeInvoice-查询待开票异常")
  142. }
  143. g.Log().Infof(ctx, "simpleMakeInvoice-本次共加载%d条开票记录", res.Len())
  144. total, okNum = res.Len(), 0
  145. for _, m := range res.List() {
  146. select {
  147. case im.runPool <- true:
  148. case <-time.After(time.Minute * 5):
  149. err = gerror.Wrap(consts.WaitTimeOut, "simpleMakeInvoice-开票等待超时")
  150. return
  151. }
  152. var (
  153. orderCode = gconv.String(m["order_code"])
  154. iType = gconv.String(m["invoice_type"])
  155. remark = gconv.String(m["remark"])
  156. prices = gconv.Float64(m["invoice_money"]) / float64(100)
  157. )
  158. if prices == 0 { //若开票金额为0 则取支付金额
  159. prices = gconv.Float64(m["pay_money"]) / float64(100)
  160. }
  161. c := MakeInvoiceData{
  162. Type: "2",
  163. Id: orderCode,
  164. Notes: remark,
  165. Fhr: g.Cfg().MustGet(ctx, "company.hfr", "贺鹏飞").String(),
  166. InvoiceArr: []MakeInvoiceItems{{
  167. Xmmc: g.Cfg().MustGet(ctx, "company.taxCode").String(), //开票项
  168. WhStatus: 1, //开票项是否维护
  169. Je: strconv.FormatFloat(prices, 'f', -1, 64), //金额
  170. Sl: "1", //数量
  171. }},
  172. }
  173. if iType == "单位" {
  174. c.Gmfmc = gconv.String(m["company_name"])
  175. c.Gmfnsrsbh = gconv.String(m["taxpayer_identnum"])
  176. } else {
  177. c.Gmfmc = iType
  178. }
  179. err = im.Auth.MakeSingleInvoice(c)
  180. if err != nil {
  181. im.ReleasePool()
  182. if gerror.Is(err, consts.LoginOutErr) {
  183. g.Log().Infof(ctx, "simpleMakeInvoice-身份过期,需要重新登录")
  184. return
  185. }
  186. g.Log().Errorf(ctx, "simpleMakeInvoice-开票接口调用异常 %v", err)
  187. continue
  188. }
  189. okNum++
  190. }
  191. return
  192. }
  193. // selfMakeInvoice 自助开票
  194. func (im *InvoiceManager) selfMakeInvoice(ctx context.Context) (total, okNum int, err error) {
  195. var (
  196. res gdb.Result
  197. )
  198. //res, err = g.DB().Query(ctx, "SELECT a.invoice_money,a.only_Identifying,a.invoice_type,a.company_name,a.taxpayer_identnum,a.remark FROM invoice a WHERE a.invoice_status=0 AND a.invoice_changed=0 AND a.invoice_variety='电子普通发票' AND a.invoice_order_code is not NULL AND a.create_time > ? GROUP BY invoice_order_code", consts.InvoiceStartTime.Unix())
  199. res, err = g.DB().Query(ctx, "SELECT a.invoice_money,a.only_Identifying,a.invoice_type,a.company_name,a.taxpayer_identnum,a.remark FROM invoice a WHERE a.invoice_status=0 AND a.invoice_changed=0 AND a.invoice_variety like '%普通发票%' AND a.invoice_order_code like '%,%' AND a.create_time > ? GROUP BY invoice_order_code", consts.InvoiceStartTime.Unix())
  200. if err != nil {
  201. return -1, -1, gerror.Wrap(err, "selfMakeInvoice-查询待开票异常")
  202. }
  203. total, okNum = res.Len(), 0
  204. for _, m := range res.List() {
  205. select {
  206. case im.runPool <- true:
  207. case <-time.After(time.Minute * 5):
  208. err = gerror.Wrap(consts.WaitTimeOut, "simpleMakeInvoice-开票等待超时")
  209. return
  210. }
  211. var (
  212. orderCode = gconv.String(m["only_Identifying"])
  213. iType = gconv.String(m["invoice_type"])
  214. prices = gconv.Float64(m["invoice_money"]) / float64(100)
  215. remark = gconv.String(m["remark"])
  216. )
  217. c := MakeInvoiceData{
  218. Type: "2",
  219. Id: orderCode,
  220. Notes: remark,
  221. Fhr: g.Cfg().MustGet(ctx, "company.hfr", "贺鹏飞").String(),
  222. InvoiceArr: []MakeInvoiceItems{{
  223. Xmmc: g.Cfg().MustGet(ctx, "company.taxCode").String(), //开票项
  224. WhStatus: 1, //开票项是否维护
  225. Je: strconv.FormatFloat(prices, 'f', -1, 64), //金额
  226. Sl: "1", //数量
  227. }},
  228. }
  229. if iType == "单位" {
  230. c.Gmfmc = gconv.String(m["company_name"])
  231. c.Gmfnsrsbh = gconv.String(m["taxpayer_identnum"])
  232. } else {
  233. c.Gmfmc = iType
  234. }
  235. err = im.Auth.MakeSingleInvoice(c)
  236. if err != nil {
  237. im.ReleasePool()
  238. if gerror.Is(err, consts.LoginOutErr) {
  239. g.Log().Infof(ctx, "selfMakeInvoice-身份过期,需要重新登录")
  240. return
  241. }
  242. g.Log().Errorf(ctx, "selfMakeInvoice-开票接口调用异常 %v", err)
  243. continue
  244. }
  245. okNum++
  246. }
  247. return
  248. }