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.

434 lines
13 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, num1 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, 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, 0, errors.New("没有工作表")
  87. }
  88. rows, err := f.GetRows("Sheet1")
  89. if err != nil {
  90. return 0, 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, 0, errors.New("参数转换失败")
  114. }
  115. jwcodes = append(jwcodes, jwcode)
  116. /*参数校验*/
  117. }
  118. /*给用户发放卡券*/
  119. num, num1, err = s.InsertCouponUsersByJwcodes(ctx, jwcodes, couponId)
  120. /*给用户发放卡券*/
  121. //return //返回jwcodes切片
  122. return num, num1, err
  123. }
  124. // 传来jwcodes字符串,解析并发放卡券
  125. func (s *sCouponUsers) InsertCouponUsersByJwcodeStr(ctx context.Context, jwcodeStr string, couponId int) (num, num1 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, 0, errors.New("参数转换失败")
  138. }
  139. jwcodesInt = append(jwcodesInt, jwcodeInt)
  140. }
  141. /*给用户发放卡券*/
  142. num, num1, err = s.InsertCouponUsersByJwcodes(ctx, jwcodesInt, couponId)
  143. /*给用户发放卡券*/
  144. return num, num1, err
  145. }
  146. // 根据精网号发放用户优惠券,群发 //不被controller层调用了,但不能删除
  147. func (s *sCouponUsers) InsertCouponUsersByJwcodes(ctx context.Context, jwcodes []int, couponId int) (num, num1 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. //检查数据库表coupon_users中是否存在
  160. count, err := dao.CouponUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Count()
  161. if err != nil {
  162. return num, num1, errors.New("检索数据库中是否已存在数据失败")
  163. }
  164. //coupon_users中不存在,可以插入
  165. if count == 0 {
  166. //判断在member_info表中是否存在
  167. count1, err := dao.MemberInfo.Ctx(ctx).Where("jwcode = ?", jwcode).Count()
  168. if err != nil {
  169. return num, num1, errors.New("检索member_info中是否已存在数据失败")
  170. }
  171. //在member_info表中存在
  172. if count1 > 0 {
  173. result, err := dao.CouponUsers.Ctx(ctx).Insert(g.Map{
  174. "jwcode": jwcode,
  175. "coupon_id": couponId,
  176. "time": gtime.Now().Unix(),
  177. })
  178. if err != nil {
  179. return num, num1, errors.New("插入数据库失败")
  180. }
  181. //获取受影响的行数
  182. affected, err := result.RowsAffected()
  183. num += int(affected)
  184. } else {
  185. //在member_info表中不存在
  186. num1++
  187. }
  188. } else {
  189. //在coupon_users中存在,即已经有卡券了
  190. num1++
  191. }
  192. }
  193. return num, num1, err //返回受影响的行数,即新增的条数
  194. }
  195. // 导入满足条件的用户jwcode到redis //不上传到redis了,上传到数据库表,到时候直接从表里查
  196. func (s *sCouponUsers) InsertJwcodesToRedisByExcel(file multipart.File) (err error) {
  197. var ctx g.Ctx
  198. //打开文件并返回一个excelize.File对象,用于读取和操作Excel文件的内容
  199. f, err := excelize.OpenReader(file)
  200. if err != nil {
  201. return errors.New("打开文件失败")
  202. }
  203. // 延时关闭文件
  204. defer func(f *excelize.File) {
  205. err := f.Close()
  206. if err != nil {
  207. // 处理关闭文件时的错误
  208. return
  209. }
  210. }(f)
  211. //读取所有工作表名称
  212. GetSheetMap := f.GetSheetMap()
  213. if len(GetSheetMap) == 0 {
  214. return errors.New("没有工作表")
  215. }
  216. // 读取第一个工作表
  217. rows, err := f.GetRows("Sheet1")
  218. if err != nil {
  219. return err
  220. }
  221. // 将每行的第一列转换为int,并添加到切片中
  222. //var jwcodes []int
  223. for i, row := range rows {
  224. //跳过第一行
  225. if i == 0 {
  226. continue
  227. }
  228. // 假设jwcode在每行的第一列
  229. /*参数校验*/
  230. //参数校验,检查每行是否有足够数据
  231. if len(row) == 0 {
  232. continue // 跳过空行
  233. }
  234. //参数校验,检查jwcode是否为非空字符串
  235. jwcodeStr := row[0]
  236. if jwcodeStr == "" {
  237. continue // 跳过空行
  238. }
  239. //将字符串转换为整数
  240. jwcode, err := strconv.Atoi(jwcodeStr)
  241. if err != nil {
  242. return errors.New("参数转换失败")
  243. }
  244. // 判断jwcode是否在表coupon_qualified_users中
  245. count, err := dao.CouponQualifiedUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Count()
  246. if err != nil {
  247. return errors.New("检索数据库中是否已存在数据失败")
  248. }
  249. if count == 0 { //不存在,插入
  250. _, err = dao.CouponQualifiedUsers.Ctx(ctx).Insert(g.Map{
  251. "jwcode": jwcode,
  252. })
  253. }
  254. //jwcodes = append(jwcodes, jwcode)
  255. /*参数校验*/
  256. }
  257. return //返回nil表示成功
  258. }
  259. // 判断某用户能否抽到卡券
  260. func (s *sCouponUsers) IsEligibleUser(ctx context.Context, jwcode int, couponIds []int) (img string, err error) {
  261. //从数据库中获取符合条件的精网号EligibleJwcodes
  262. var EligibleJwcodes []couponusers.InsertCouponUserRes
  263. err = dao.CouponQualifiedUsers.Ctx(ctx).Fields("jwcode").Scan(&EligibleJwcodes)
  264. if err != nil {
  265. return "", errors.New("从数据库中获取符合条件的精网号失败")
  266. }
  267. if len(EligibleJwcodes) == 0 {
  268. return "", errors.New("核验数据库(满足条件)中数据为空")
  269. }
  270. //检查jwcode是否在EligibleJwcodes中
  271. for _, EligibleJwcode := range EligibleJwcodes {
  272. if EligibleJwcode.Jwcode == jwcode {
  273. //存在,有资格,判断是否抽取过
  274. for _, couponId := range couponIds {
  275. count, err := dao.CouponUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Count()
  276. if err != nil {
  277. return "", errors.New("检索数据库中是否已存在数据失败")
  278. }
  279. //有记录,抽取过
  280. if count > 0 {
  281. err = dao.CouponUsers.Ctx(ctx).Fields("img_url").Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Scan(&img)
  282. return img, errors.New("该用户已领取过该卡券")
  283. }
  284. }
  285. //所有的都没有记录,没有抽取过
  286. return "", nil
  287. }
  288. }
  289. return "", errors.New("该用户不满足领取条件")
  290. }
  291. // 给单个用户发放卡券
  292. func (s *sCouponUsers) IssueCouponToUser(ctx context.Context, jwcode, couponId int) (err error) {
  293. //查看库中是否已经存在
  294. count, err := dao.CouponUsers.Ctx(ctx).Where("jwcode = ?", jwcode).Where("coupon_id = ?", couponId).Count()
  295. if err != nil {
  296. return errors.New("检索数据库中是否已存在数据失败")
  297. }
  298. //已存在 不添加,直接返回
  299. if count > 0 {
  300. return errors.New("该用户已领取该卡券")
  301. }
  302. //不存在 查看是否满足条件,满足就添加,不满足就返回
  303. //从数据库中获取符合条件的精网号EligibleJwcodes
  304. var EligibleJwcodes []couponusers.InsertCouponUserRes
  305. err = dao.CouponQualifiedUsers.Ctx(ctx).Fields("jwcode").Scan(&EligibleJwcodes)
  306. if err != nil {
  307. return errors.New("从数据库中获取符合条件的精网号失败")
  308. }
  309. if len(EligibleJwcodes) == 0 {
  310. return errors.New("核验数据库(满足条件)中数据为空")
  311. }
  312. //检查jwcode是否在EligibleJwcodes中
  313. for _, EligibleJwcode := range EligibleJwcodes {
  314. if EligibleJwcode.Jwcode == jwcode {
  315. //存在,可以插入
  316. _, err := dao.CouponUsers.Ctx(ctx).Insert(g.Map{
  317. "jwcode": jwcode,
  318. "coupon_id": couponId,
  319. "time": gtime.Now().Unix(),
  320. })
  321. if err != nil {
  322. return errors.New("插入数据库失败")
  323. }
  324. return err
  325. }
  326. }
  327. return errors.New("该用户精网号不符合领取条件") //遍历完了所有满足条件的用户,发现不在其中,不符合条件
  328. }
  329. // 导出拥有卡券的用户列表
  330. func (s *sCouponUsers) ExportCouponUsers(r *ghttp.Request, couponId, jwcode int, name string, pageNo, pageSize int) {
  331. //调用查询
  332. users, err := s.GetCouponUsersByCondition(r.Context(), couponId, jwcode, name, pageNo, pageSize)
  333. if err != nil {
  334. r.Response.WriteJsonExit(dto.Error("查询失败:" + err.Error()))
  335. return
  336. }
  337. if len(users) == 0 {
  338. r.Response.WriteJsonExit(dto.Error("查询结果为空"))
  339. return
  340. }
  341. //创建 Excel文件
  342. excelFile := excelize.NewFile() //默认会创建一个Sheet1
  343. sheetName := "Sheet1"
  344. //设置表头
  345. headers := []string{"序号", "精网号", "姓名", "部门", "门店"}
  346. for i, header := range headers {
  347. col := string('A' + i) //将索引转换为 Excel列名
  348. excelFile.SetCellValue(sheetName, col+"1", header)
  349. }
  350. /*写入数据*/
  351. rowIndex := 2 //从第二行开始写入数据
  352. for _, user := range users {
  353. excelFile.SetCellValue(sheetName, "A"+strconv.Itoa(rowIndex), rowIndex-1) //序号
  354. excelFile.SetCellValue(sheetName, "B"+strconv.Itoa(rowIndex), user.Jwcode) //精网号
  355. excelFile.SetCellValue(sheetName, "C"+strconv.Itoa(rowIndex), user.Name) //姓名
  356. excelFile.SetCellValue(sheetName, "D"+strconv.Itoa(rowIndex), user.DeptName) //部门
  357. excelFile.SetCellValue(sheetName, "E"+strconv.Itoa(rowIndex), user.ShopName) //门店
  358. rowIndex++
  359. }
  360. /*写入数据*/
  361. //设置文件名
  362. fileName := "Users_" + time.Now().Format("20060102150405") + ".xlsx"
  363. //保存到缓冲区并返回
  364. buffer, err := excelFile.WriteToBuffer()
  365. if err != nil {
  366. r.Response.WriteJsonExit(dto.Error("生成Excel文件失败:" + err.Error()))
  367. return
  368. }
  369. // 设置响应头,指定内容类型为Excel文件
  370. r.Response.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
  371. // 设置响应头,指定文件名为fileName
  372. r.Response.Header().Set("Content-Disposition", "attachment; filename="+fileName)
  373. // 将buffer中的内容写入响应
  374. r.Response.Write(buffer.Bytes())
  375. }
  376. // 添加用户选择武器记录
  377. func (s *sCouponUsers) AddRecord(ctx context.Context, jwcode int, id int, name string) (err error) {
  378. _, err = dao.CouponUsers.Ctx(ctx).Data(g.Map{
  379. "record": name,
  380. }).Where("jwcode = ? and coupon_id = ?", jwcode, id).Update()
  381. if err != nil {
  382. return errors.New("添加武器记录失败")
  383. }
  384. return
  385. }