You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

410 lines
12 KiB

  1. package couponusers
  2. import (
  3. "CouponBackendGo/api/v1/couponusers"
  4. "CouponBackendGo/internal/dao"
  5. "CouponBackendGo/internal/model/dto"
  6. "CouponBackendGo/internal/service"
  7. "context"
  8. "database/sql"
  9. "errors"
  10. "github.com/gogf/gf/v2/frame/g"
  11. "github.com/gogf/gf/v2/net/ghttp"
  12. "github.com/gogf/gf/v2/os/gtime"
  13. "github.com/xuri/excelize/v2"
  14. "mime/multipart"
  15. "strconv"
  16. "strings"
  17. "time"
  18. )
  19. type sCouponUsers struct{}
  20. func init() {
  21. service.RegisterCouponUsers(&sCouponUsers{})
  22. }
  23. // 根据优惠券id查看拥有优惠券的用户
  24. func (s *sCouponUsers) GetCouponUsersByCondition(ctx context.Context, couponId, jwcode int, name string, pageNo, pageSize int) (users []couponusers.GetCouponUsersRes, err error) {
  25. db := dao.CouponUsers.Ctx(ctx)
  26. //基于量表JOIN查询
  27. db = db.As("cu").InnerJoin("member_info mi", "cu.jwcode = mi.jwcode").
  28. Where("cu.coupon_id = ?", couponId)
  29. //如果jwcode条件存在
  30. if jwcode != 0 {
  31. db = db.Where("cu.jwcode = ?", jwcode)
  32. }
  33. //如果name条件存在
  34. if name != "" {
  35. db = db.Where("mi.name like ?", "%"+name+"%")
  36. }
  37. //查询总数
  38. total, err := db.Count()
  39. if err != nil {
  40. return nil, errors.New("查询总数失败")
  41. }
  42. //查询数据(分页)
  43. err = db.Fields("mi.jwcode, mi.name, mi.deptName, mi.shopName").
  44. Page(pageNo, pageSize).Scan(&users)
  45. if err != nil {
  46. return nil, errors.New("查询数据失败")
  47. }
  48. //设置总数
  49. for i := range users {
  50. users[i].Total = total
  51. }
  52. return users, err
  53. }
  54. // 根据jwcode,优惠券id删除用户
  55. func (s *sCouponUsers) DeleteCouponUserByJwcode(ctx context.Context, couponId, jwcode int) (result sql.Result, err error) {
  56. if jwcode == 0 {
  57. return nil, errors.New("jwcode不能为空")
  58. }
  59. if couponId == 0 {
  60. return nil, errors.New("couponId不能为空")
  61. }
  62. result, err = dao.CouponUsers.Ctx(ctx).Where("coupon_id = ?", couponId).Where("jwcode = ?", jwcode).Delete()
  63. if err != nil {
  64. return nil, errors.New("删除用户失败")
  65. }
  66. return result, err
  67. }
  68. // 通过excel导入精网号
  69. func (s *sCouponUsers) InsertJwcodeByExcel(ctx context.Context, file multipart.File, couponId int) (num int, err error) {
  70. // func (s *sCouponUsers) InsertJwcodeByExcel(file multipart.File) (jwcodes []int, err error) {
  71. //打开文件并返回一个excelize.File对象,用于读取和操作Excel文件的内容
  72. f, err := excelize.OpenReader(file)
  73. if err != nil {
  74. return 0, errors.New("打开文件失败")
  75. }
  76. // 延时关闭文件
  77. defer func(f *excelize.File) {
  78. err := f.Close()
  79. if err != nil {
  80. return // 返回错误
  81. }
  82. }(f)
  83. //读取所有工作表名称
  84. GetSheetMap := f.GetSheetMap()
  85. if len(GetSheetMap) == 0 {
  86. return 0, errors.New("没有工作表")
  87. }
  88. rows, err := f.GetRows("Sheet1")
  89. if err != nil {
  90. return 0, err
  91. }
  92. // 将每行的第一列转换为int,并添加到切片中
  93. var jwcodes []int
  94. for i, row := range rows {
  95. //跳过第一行
  96. if i == 0 {
  97. continue
  98. }
  99. // 假设jwcode在每行的第一列
  100. /*参数校验*/
  101. //参数校验,检查每行是否有足够数据
  102. if len(row) == 0 {
  103. continue // 跳过空行
  104. }
  105. //参数校验,检查jwcode是否为非空字符串
  106. jwcodeStr := row[0]
  107. if jwcodeStr == "" {
  108. continue // 跳过空行
  109. }
  110. //将字符串转换为整数
  111. jwcode, err := strconv.Atoi(jwcodeStr)
  112. if err != nil {
  113. return 0, errors.New("参数转换失败")
  114. }
  115. jwcodes = append(jwcodes, jwcode)
  116. /*参数校验*/
  117. }
  118. /*给用户发放卡券*/
  119. num, err = s.InsertCouponUsersByJwcodes(ctx, jwcodes, couponId)
  120. /*给用户发放卡券*/
  121. //return //返回jwcodes切片
  122. return num, err
  123. }
  124. // 传来jwcodes字符串,解析并发放卡券
  125. func (s *sCouponUsers) InsertCouponUsersByJwcodeStr(ctx context.Context, jwcodeStr string, couponId int) (num int, err error) {
  126. //将字符串转换为切片
  127. jwcodes := strings.Split(jwcodeStr, ",")
  128. //转换成int
  129. var jwcodesInt []int
  130. for _, jwcode := range jwcodes {
  131. //参数校验,检查jwcode是否为非空字符串
  132. if jwcode == "" {
  133. continue
  134. }
  135. jwcodeInt, err := strconv.Atoi(jwcode)
  136. if err != nil {
  137. return 0, errors.New("参数转换失败")
  138. }
  139. jwcodesInt = append(jwcodesInt, jwcodeInt)
  140. }
  141. /*给用户发放卡券*/
  142. num, err = s.InsertCouponUsersByJwcodes(ctx, jwcodesInt, couponId)
  143. /*给用户发放卡券*/
  144. return
  145. }
  146. // 根据精网号发放用户优惠券,群发 //不被controller层调用了,但不能删除
  147. func (s *sCouponUsers) InsertCouponUsersByJwcodes(ctx context.Context, jwcodes []int, couponId int) (num int, err error) {
  148. //去重
  149. m := make(map[int]bool)
  150. var uniqueJwcodes []int //存放去重后的精网号
  151. for _, jwcode := range jwcodes {
  152. if _, exist := m[jwcode]; !exist {
  153. m[jwcode] = true
  154. uniqueJwcodes = append(uniqueJwcodes, jwcode)
  155. }
  156. }
  157. //插入数据
  158. for _, jwcode := range uniqueJwcodes {
  159. //检查数据库中是否存在
  160. count, err := dao.CouponUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Count()
  161. if err != nil {
  162. return num, errors.New("检索数据库中是否已存在数据失败")
  163. }
  164. //不存在,可以插入
  165. if count == 0 {
  166. result, err := dao.CouponUsers.Ctx(ctx).Insert(g.Map{
  167. "jwcode": jwcode,
  168. "coupon_id": couponId,
  169. "time": gtime.Now().Unix(),
  170. })
  171. if err != nil {
  172. return num, errors.New("插入数据库失败")
  173. }
  174. //获取受影响的行数
  175. affected, err := result.RowsAffected()
  176. num += int(affected)
  177. }
  178. }
  179. return num, err //返回受影响的行数,即新增的条数
  180. }
  181. // 导入满足条件的用户jwcode到redis //不上传到redis了,上传到数据库表,到时候直接从表里查
  182. func (s *sCouponUsers) InsertJwcodesToRedisByExcel(file multipart.File) (err error) {
  183. var ctx g.Ctx
  184. //打开文件并返回一个excelize.File对象,用于读取和操作Excel文件的内容
  185. f, err := excelize.OpenReader(file)
  186. if err != nil {
  187. return errors.New("打开文件失败")
  188. }
  189. // 延时关闭文件
  190. defer func(f *excelize.File) {
  191. err := f.Close()
  192. if err != nil {
  193. // 处理关闭文件时的错误
  194. return
  195. }
  196. }(f)
  197. //读取所有工作表名称
  198. GetSheetMap := f.GetSheetMap()
  199. if len(GetSheetMap) == 0 {
  200. return errors.New("没有工作表")
  201. }
  202. // 读取第一个工作表
  203. rows, err := f.GetRows("Sheet1")
  204. if err != nil {
  205. return err
  206. }
  207. // 将每行的第一列转换为int,并添加到切片中
  208. //var jwcodes []int
  209. for i, row := range rows {
  210. //跳过第一行
  211. if i == 0 {
  212. continue
  213. }
  214. // 假设jwcode在每行的第一列
  215. /*参数校验*/
  216. //参数校验,检查每行是否有足够数据
  217. if len(row) == 0 {
  218. continue // 跳过空行
  219. }
  220. //参数校验,检查jwcode是否为非空字符串
  221. jwcodeStr := row[0]
  222. if jwcodeStr == "" {
  223. continue // 跳过空行
  224. }
  225. //将字符串转换为整数
  226. jwcode, err := strconv.Atoi(jwcodeStr)
  227. if err != nil {
  228. return errors.New("参数转换失败")
  229. }
  230. // 判断jwcode是否在表coupon_qualified_users中
  231. count, err := dao.CouponQualifiedUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Count()
  232. if err != nil {
  233. return errors.New("检索数据库中是否已存在数据失败")
  234. }
  235. if count == 0 { //不存在,插入
  236. _, err = dao.CouponQualifiedUsers.Ctx(ctx).Insert(g.Map{
  237. "jwcode": jwcode,
  238. })
  239. }
  240. //jwcodes = append(jwcodes, jwcode)
  241. /*参数校验*/
  242. }
  243. return //返回nil表示成功
  244. }
  245. // 判断某用户能否抽到卡券
  246. func (s *sCouponUsers) IsEligibleUser(ctx context.Context, jwcode int, couponIds []int) (img string, err error) {
  247. //从数据库中获取符合条件的精网号EligibleJwcodes
  248. var EligibleJwcodes []couponusers.InsertCouponUserRes
  249. err = dao.CouponQualifiedUsers.Ctx(ctx).Fields("jwcode").Scan(&EligibleJwcodes)
  250. if err != nil {
  251. return "", errors.New("从数据库中获取符合条件的精网号失败")
  252. }
  253. if len(EligibleJwcodes) == 0 {
  254. return "", errors.New("核验数据库(满足条件)中数据为空")
  255. }
  256. //检查jwcode是否在EligibleJwcodes中
  257. for _, EligibleJwcode := range EligibleJwcodes {
  258. if EligibleJwcode.Jwcode == jwcode {
  259. //存在,有资格,判断是否抽取过
  260. for _, couponId := range couponIds {
  261. count, err := dao.CouponUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Count()
  262. if err != nil {
  263. return "", errors.New("检索数据库中是否已存在数据失败")
  264. }
  265. //有记录,抽取过
  266. if count > 0 {
  267. err = dao.CouponUsers.Ctx(ctx).Fields("img_url").Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Scan(&img)
  268. return img, errors.New("该用户已领取过该卡券")
  269. }
  270. }
  271. //所有的都没有记录,没有抽取过
  272. return "", nil
  273. }
  274. }
  275. return "", errors.New("该用户不满足领取条件")
  276. }
  277. // 给单个用户发放卡券
  278. func (s *sCouponUsers) IssueCouponToUser(ctx context.Context, jwcode, couponId int) (err error) {
  279. //查看库中是否已经存在
  280. count, err := dao.CouponUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Count()
  281. if err != nil {
  282. return errors.New("检索数据库中是否已存在数据失败")
  283. }
  284. //已存在 不添加,直接返回
  285. if count > 0 {
  286. return errors.New("该用户已领取该卡券")
  287. }
  288. //不存在 查看是否满足条件,满足就添加,不满足就返回
  289. //从数据库中获取符合条件的精网号EligibleJwcodes
  290. var EligibleJwcodes []couponusers.InsertCouponUserRes
  291. err = dao.CouponQualifiedUsers.Ctx(ctx).Fields("jwcode").Scan(&EligibleJwcodes)
  292. if err != nil {
  293. return errors.New("从数据库中获取符合条件的精网号失败")
  294. }
  295. if len(EligibleJwcodes) == 0 {
  296. return errors.New("核验数据库(满足条件)中数据为空")
  297. }
  298. //检查jwcode是否在EligibleJwcodes中
  299. for _, EligibleJwcode := range EligibleJwcodes {
  300. if EligibleJwcode.Jwcode == jwcode {
  301. //存在,可以插入
  302. _, err := dao.CouponUsers.Ctx(ctx).Insert(g.Map{
  303. "jwcode": jwcode,
  304. "coupon_id": couponId,
  305. "time": gtime.Now().Unix(),
  306. })
  307. if err != nil {
  308. return errors.New("插入数据库失败")
  309. }
  310. return err
  311. }
  312. }
  313. return errors.New("该用户精网号不符合领取条件") //遍历完了所有满足条件的用户,发现不在其中,不符合条件
  314. }
  315. /*未编写*/
  316. // 导出拥有卡券的用户列表
  317. func (s *sCouponUsers) ExportCouponUsers(r *ghttp.Request, couponId, jwcode int, name string, pageNo, pageSize int) {
  318. //调用查询
  319. users, err := s.GetCouponUsersByCondition(r.Context(), couponId, jwcode, name, pageNo, pageSize)
  320. if err != nil {
  321. r.Response.WriteJsonExit(dto.Error("查询失败:" + err.Error()))
  322. return
  323. }
  324. if len(users) == 0 {
  325. r.Response.WriteJsonExit(dto.Error("查询结果为空"))
  326. return
  327. }
  328. //创建 Excel文件
  329. excelFile := excelize.NewFile() //默认会创建一个Sheet1
  330. sheetName := "Sheet1"
  331. //设置表头
  332. headers := []string{"序号", "精网号", "姓名", "部门", "门店"}
  333. for i, header := range headers {
  334. col := string('A' + i) //将索引转换为 Excel列名
  335. excelFile.SetCellValue(sheetName, col+"1", header)
  336. }
  337. /*写入数据*/
  338. rowIndex := 2 //从第二行开始写入数据
  339. for _, user := range users {
  340. excelFile.SetCellValue(sheetName, "A"+strconv.Itoa(rowIndex), rowIndex-1) //序号
  341. excelFile.SetCellValue(sheetName, "B"+strconv.Itoa(rowIndex), user.Jwcode) //精网号
  342. excelFile.SetCellValue(sheetName, "C"+strconv.Itoa(rowIndex), user.Name) //姓名
  343. excelFile.SetCellValue(sheetName, "D"+strconv.Itoa(rowIndex), user.DeptName) //部门
  344. excelFile.SetCellValue(sheetName, "E"+strconv.Itoa(rowIndex), user.ShopName) //门店
  345. rowIndex++
  346. }
  347. /*写入数据*/
  348. //设置文件名
  349. fileName := "Users_" + time.Now().Format("20060102150405") + ".xlsx"
  350. //保存到缓冲区并返回
  351. buffer, err := excelFile.WriteToBuffer()
  352. if err != nil {
  353. r.Response.WriteJsonExit(dto.Error("生成Excel文件失败:" + err.Error()))
  354. return
  355. }
  356. // 设置响应头,指定内容类型为Excel文件
  357. r.Response.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
  358. // 设置响应头,指定文件名为fileName
  359. r.Response.Header().Set("Content-Disposition", "attachment; filename="+fileName)
  360. // 将buffer中的内容写入响应
  361. r.Response.Write(buffer.Bytes())
  362. }