diff --git a/app/admin/apis/erpordermanage/erp_market.go b/app/admin/apis/erpordermanage/erp_market.go new file mode 100644 index 0000000..a4fa0b2 --- /dev/null +++ b/app/admin/apis/erpordermanage/erp_market.go @@ -0,0 +1,147 @@ +package erpordermanage + +import ( + "errors" + "github.com/gin-gonic/gin" + "go-admin/app/admin/models" + orm "go-admin/common/global" + "go-admin/logger" + "go-admin/tools" + "go-admin/tools/app" + "gorm.io/gorm" + "net/http" + "time" +) + +// CreateOrUpdateBusinessSummary 创建或更新每日经营数据 +// @Summary 创建或更新每日经营数据 +// @Tags 商场检查 +// @Produce json +// @Accept json +// @Param request body models.CreateBusinessSummaryRequest true "每日经营数据模型" +// @Success 200 {object} app.Response +// @Router /api/v1/erp_order/business/create [post] +func CreateOrUpdateBusinessSummary(c *gin.Context) { + var req models.CreateBusinessSummaryRequest + + if err := c.ShouldBindJSON(&req); err != nil { + app.Error(c, 400, err, err.Error()) + return + } + + err := tools.Validate(req) + if err != nil { + app.Error(c, 400, err, err.Error()) + return + } + + summaryDate, err := time.Parse("2006-01-02", req.Date) + if err != nil { + app.Error(c, 400, err, "日期格式不正确,应为 YYYY-MM-DD") + return + } + + var existing models.DailyBusinessSummary + err = orm.Eloquent. + Where("date = ? AND store_id = ?", summaryDate, req.StoreID). + First(&existing).Error + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + // 不存在,新增 + newRecord := models.DailyBusinessSummary{ + Date: summaryDate, + StoreID: req.StoreID, + SalesAmount: req.SalesAmount, + PromotionFee: req.PromotionFee, + SalesProfit: req.SalesProfit, + StaffProfit: req.StaffProfit, + Count: req.Count, + } + if err := orm.Eloquent.Create(&newRecord).Error; err != nil { + app.Error(c, 500, err, "创建失败") + return + } + app.OK(c, newRecord, "创建成功") + return + } else { + // 其他错误 + app.Error(c, 500, err, "查询失败") + return + } + } + + // 存在,更新 + updateMap := map[string]interface{}{ + "sales_amount": req.SalesAmount, + "promotion_fee": req.PromotionFee, + "sales_profit": req.SalesProfit, + "staff_profit": req.StaffProfit, + "count": req.Count, + } + + if err := orm.Eloquent.Model(&existing). + Updates(updateMap).Error; err != nil { + app.Error(c, 500, err, "更新失败") + return + } + + app.OK(c, existing, "更新成功") +} + +// ListBusinessSummary 获取每日经营数据列表( +// @Summary 获取每日经营数据列表 +// @Tags 商场检查 +// @Accept json +// @Produce json +// @Param request body models.BusinessSummaryListRequest true "获取每日经营数据列表请求参数" +// @Success 200 {object} models.BusinessSummaryListResp +// @Router /api/v1/erp_order/business/list [post] +func ListBusinessSummary(c *gin.Context) { + var req = new(models.BusinessSummaryListRequest) + if err := c.ShouldBindJSON(&req); err != nil { + app.Error(c, 400, err, "参数错误") + return + } + + resp, err := models.QueryListBusinessSummary(req, c) + if err != nil { + logger.Error("QueryListBusinessSummary err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) + return + } + + app.OK(c, resp, "ok") +} + +// ListStoreManageData 查询门店经营汇总数据 +// @Summary 查询门店经营汇总数据 +// @Tags 商场检查 +// @Produce json +// @Accept json +// @Param request body models.MarketStoreSalesDataReq true "查询门店经营数据模型" +// @Success 200 {object} models.MarketStoreSalesDataResp +// @Router /api/v1/erp_order/business/summary_data [post] +func ListStoreManageData(c *gin.Context) { + var req = new(models.MarketStoreSalesDataReq) + if err := c.ShouldBindJSON(&req); err != nil { + logger.Error("ShouldBindJSON err:", logger.Field("err", err)) + app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error()) + return + } + + err := tools.Validate(req) //必填参数校验 + if err != nil { + app.Error(c, http.StatusBadRequest, err, err.Error()) + return + } + + resp, err := models.QueryDailyBusinessSummaryData(req, c) + if err != nil { + logger.Error("QueryDailyBusinessSummaryData err:", logger.Field("err", err)) + app.Error(c, http.StatusInternalServerError, err, "查询失败:"+err.Error()) + return + } + + app.OK(c, resp, "") +} diff --git a/app/admin/apis/repair/repair.go b/app/admin/apis/repair/repair.go index 845f971..cce4ee3 100644 --- a/app/admin/apis/repair/repair.go +++ b/app/admin/apis/repair/repair.go @@ -496,3 +496,35 @@ func CompleteRepair(c *gin.Context) { app.OK(c, nil, "操作成功") return } + +// OrderDetailReport 维修明细 +// @Summary 维修明细 +// @Tags 维修管理,V1.4.6 +// @Produce json +// @Accept json +// @Param request body models.ErpRepairDetailReportReq true "维修明细模型" +// @Success 200 {object} models.ErpRepairDetailReportResp +// @Router /api/v1/repair/detail_report [post] +func OrderDetailReport(c *gin.Context) { + req := &model.ErpRepairDetailReportReq{} + if err := c.ShouldBindJSON(&req); err != nil { + //logger.Error(err) + app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误:"+err.Error()) + return + } + + err := tools.Validate(req) //必填参数校验 + if err != nil { + app.Error(c, http.StatusBadRequest, err, err.Error()) + return + } + + resp, err := model.RepairDetailReport(req, c) + if err != nil { + app.Error(c, http.StatusInternalServerError, err, "操作失败:"+err.Error()) + return + } + + app.OK(c, resp, "查询成功") + return +} diff --git a/app/admin/models/erp_market.go b/app/admin/models/erp_market.go new file mode 100644 index 0000000..b379f62 --- /dev/null +++ b/app/admin/models/erp_market.go @@ -0,0 +1,600 @@ +package models + +import ( + "errors" + "fmt" + "github.com/gin-gonic/gin" + "github.com/xuri/excelize/v2" + orm "go-admin/common/global" + "go-admin/logger" + "go-admin/tools" + "go-admin/tools/config" + "math" + "strconv" + "time" +) + +// DailyBusinessSummary 每日经营数据汇总表 +type DailyBusinessSummary struct { + Model + + Date time.Time `json:"date"` // 汇总日期 + StoreID uint `json:"store_id"` // 门店ID + SalesAmount float64 `json:"sales_amount"` // 销售额 + PromotionFee float64 `json:"promotion_fee"` // 推广费 + SalesProfit float64 `json:"sales_profit"` // 销售毛利 + StaffProfit float64 `json:"staff_profit"` // 员工毛利 + Count int32 `json:"count"` // 销售数量 +} + +type CreateBusinessSummaryRequest struct { + Date string `json:"date" binding:"required"` // 格式:yyyy-MM-dd + StoreID uint `json:"store_id" binding:"required"` // 门店id + SalesAmount float64 `json:"sales_amount"` // 销售额 + PromotionFee float64 `json:"promotion_fee"` // 推广费 + SalesProfit float64 `json:"sales_profit"` // 销售毛利 + StaffProfit float64 `json:"staff_profit"` // 员工毛利 + Count int32 `json:"count"` // 销售数量 +} + +type BusinessSummaryListRequest struct { + StoreId []uint `json:"store_id"` // 可选,复选门店ID + StartTime string `json:"start_time"` // 开始时间 + EndTime string `json:"end_time"` // 结束时间 + PageIndex int `json:"page_index"` // 当前页码 + PageSize int `json:"page_size"` // 每页条数 +} + +type BusinessSummaryItem struct { + Date string `json:"date"` // 时间,如:"2023-12-25" + SalesAmount float64 `json:"sales_amount"` // 销售额 + PromotionFee float64 `json:"promotion_fee"` // 推广费 + SalesProfit float64 `json:"sales_profit"` // 销售毛利 + StaffProfit float64 `json:"staff_profit"` // 员工毛利 + Count int32 `json:"count"` // 销售数量 +} + +type BusinessSummaryListResp struct { + List []BusinessSummaryItem `json:"list"` + Total int `json:"total"` + PageIndex int `json:"pageIndex"` + PageSize int `json:"pageSize"` + TotalSalesAmount float64 `json:"total_sales_amount"` // 总销售额 + TotalPromotionFee float64 `json:"total_promotion_fee"` // 总推广费 + TotalSalesProfit float64 `json:"total_sales_profit"` // 总销售毛利 + TotalStaffProfit float64 `json:"total_staff_profit"` // 总员工毛利 + TotalCount int64 `json:"total_count"` // 总销售数量 +} + +// MarketStoreSalesDataReq 门店销售对比入参 +type MarketStoreSalesDataReq struct { + StoreId []uint32 `json:"store_id"` // 门店ID + StartTime string `json:"start_time"` // 开始时间 + EndTime string `json:"end_time"` // 结束时间 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 页面条数 + IsExport uint32 `json:"is_export"` // 1-导出 + SortType string `json:"sort_type"` // 排序类型:desc 降序、asc 升序 +} + +// MarketStoreSalesDataResp 门店销售对比出参 +type MarketStoreSalesDataResp struct { + List []MarketStoreSalesData `json:"list"` + Total int `json:"total"` // 总条数 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 每页展示条数 + ExportUrl string `json:"export_url"` + TotalSalesAmount float64 `json:"total_sales_amount"` // 总销售额 + TotalPromotionFee float64 `json:"total_promotion_fee"` // 总推广费 + TotalSalesProfit float64 `json:"total_sales_profit"` // 总销售毛利 + TotalStaffProfit float64 `json:"total_staff_profit"` // 总员工毛利 + TotalCount int64 `json:"total_count"` // 总销售数量 +} + +// MarketStoreSalesData 门店销售数据 +type MarketStoreSalesData struct { + StoreId uint32 `json:"store_id"` // 门店id + StoreName string `json:"store_name"` // 门店名称 + SalesAmount float64 `json:"sales_amount"` // 销售额 + PromotionFee float64 `json:"promotion_fee"` // 推广费 + SalesProfit float64 `json:"sales_profit"` // 销售毛利 + StaffProfit float64 `json:"staff_profit"` // 员工毛利 + Count int64 `json:"count"` // 销售数量 +} + +// parseToDate 解析多种格式的时间字符串,仅保留日期部分(年月日) +func parseToDate(s string) (time.Time, error) { + formats := []string{ + "2006-01-02", // 标准日期格式 + time.RFC3339, // 带时区时间戳 2025-05-20T00:00:00+08:00 + "2006-01-02 15:04:05", // 常见完整格式 + } + + for _, layout := range formats { + if t, err := time.Parse(layout, s); err == nil { + // 格式化为 YYYY-MM-DD 再转换,去除时间部分 + return time.Parse("2006-01-02", t.Format("2006-01-02")) + } + } + + return time.Time{}, fmt.Errorf("无法解析日期格式: %s", s) +} + +func QueryListBusinessSummary(req *BusinessSummaryListRequest, c *gin.Context) (*BusinessSummaryListResp, error) { + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + + resp := &BusinessSummaryListResp{ + PageIndex: req.PageIndex, + PageSize: req.PageSize, + } + + startDate, err1 := parseToDate(req.StartTime) + endDate, err2 := parseToDate(req.EndTime) + if err1 != nil || err2 != nil { + return nil, errors.New("日期格式错误") + } + if endDate.Before(startDate) { + return nil, errors.New("结束时间早于开始时间") + } + + // 生成所有日期(用于补0和分页) + var allDates []string + for d := startDate; !d.After(endDate); d = d.AddDate(0, 0, 1) { + allDates = append(allDates, d.Format("2006-01-02")) + } + totalDays := len(allDates) + + // 日期分页 + start := page * req.PageSize + end := start + req.PageSize + if start > totalDays { + start = totalDays + } + if end > totalDays { + end = totalDays + } + pagedDates := allDates[start:end] + + // 查询聚合数据 + type DailyAggregatedData struct { + Date string `json:"summary_date"` + SalesAmount float64 `json:"sales_amount"` + PromotionFee float64 `json:"promotion_fee"` + SalesProfit float64 `json:"sales_profit"` + StaffProfit float64 `json:"staff_profit"` + Count int32 `json:"count"` + } + var dataList []DailyAggregatedData + + qs := orm.Eloquent.Table("daily_business_summary") + es := orm.Eloquent.Table("daily_business_summary") + + if req.StartTime != "" && req.EndTime != "" { + startTime, err := time.Parse(QueryTimeFormat, req.StartTime) + if err != nil { + logger.Error("startTime parse err") + } + + endTime, err := time.Parse(QueryTimeFormat, req.EndTime) + if err != nil { + logger.Error("endTime parse err") + } + + qs = qs.Where("date BETWEEN ? AND ?", startTime, endTime) + es = es.Where("date BETWEEN ? AND ?", startTime, endTime) + } + + // 非管理员才判断所属门店 + if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + sysUser, err := GetSysUserByCtx(c) + if err != nil { + return nil, errors.New("操作失败:" + err.Error()) + } + + // 返回sysUser未过期的门店id列表 + storeList := GetValidStoreIDs(sysUser.StoreData) + if len(storeList) > 0 { + if len(storeList) == 1 { + qs = qs.Where("store_id = ?", storeList[0]) + es = es.Where("store_id = ?", storeList[0]) + } else { + qs = qs.Where("store_id IN (?)", storeList) + es = es.Where("store_id IN (?)", storeList) + } + } else { + return nil, errors.New("用户未绑定门店") + } + } + + if len(req.StoreId) > 0 { + qs = qs.Where("store_id IN ?", req.StoreId) + es = es.Where("store_id IN ?", req.StoreId) + } + + err := qs.Select("DATE_FORMAT(date, '%Y-%m-%d') AS date, " + + "SUM(promotion_fee) AS promotion_fee, " + + "SUM(sales_amount) AS sales_amount, " + + "SUM(sales_profit) AS sales_profit, " + + "SUM(staff_profit) AS staff_profit, " + + "SUM(count) AS count"). + Group("date"). + Order("date ASC"). + Find(&dataList).Error + + if err != nil { + return nil, err + } + + // 查询汇总数据 + var summary struct { + TotalSalesAmount float64 + TotalPromotionFee float64 + TotalSalesProfit float64 + TotalStaffProfit float64 + TotalCount int64 + } + err = es.Select("SUM(sales_amount) AS total_sales_amount, " + + "SUM(promotion_fee) AS total_promotion_fee, " + + "SUM(sales_profit) AS total_sales_profit, " + + "SUM(staff_profit) AS total_staff_profit, " + + "SUM(count) AS total_count"). + Find(&summary).Error + if err != nil { + return nil, err + } + + // 聚合结果映射:date → 汇总值 + resultMap := make(map[string]DailyAggregatedData) + for _, d := range dataList { + resultMap[d.Date] = d + } + + // 构造分页结果(补零) + var result []BusinessSummaryItem + for _, dateStr := range pagedDates { + if d, ok := resultMap[dateStr]; ok { + result = append(result, BusinessSummaryItem{ + Date: dateStr, + SalesAmount: d.SalesAmount, + PromotionFee: d.PromotionFee, + SalesProfit: d.SalesProfit, + StaffProfit: d.StaffProfit, + Count: d.Count, + }) + } else { + // 补0 + result = append(result, BusinessSummaryItem{ + Date: dateStr, + SalesAmount: 0, + PromotionFee: 0, + SalesProfit: 0, + StaffProfit: 0, + Count: 0, + }) + } + } + + resp.List = result + resp.Total = totalDays + resp.PageIndex = req.PageIndex + resp.PageSize = req.PageSize + + resp.TotalSalesAmount = math.Round(summary.TotalSalesAmount*100) / 100 + resp.TotalPromotionFee = math.Round(summary.TotalPromotionFee*100) / 100 + resp.TotalSalesProfit = math.Round(summary.TotalSalesProfit*100) / 100 + resp.TotalStaffProfit = math.Round(summary.TotalStaffProfit*100) / 100 + resp.TotalCount = summary.TotalCount + + return resp, nil +} + +func QueryDailyBusinessSummaryData(req *MarketStoreSalesDataReq, c *gin.Context) (*MarketStoreSalesDataResp, error) { + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + + resp := &MarketStoreSalesDataResp{ + PageIndex: req.PageIndex, + PageSize: req.PageSize, + } + var storeManageDataList []MarketStoreSalesData + + // 查询原始数据 + qs := orm.Eloquent.Table("daily_business_summary") + + var storeList []uint32 + // 非管理员才判断所属门店 + if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + sysUser, err := GetSysUserByCtx(c) + if err != nil { + return nil, errors.New("操作失败:" + err.Error()) + } + + // 返回sysUser未过期的门店id列表 + storeList = GetValidStoreIDs(sysUser.StoreData) + if len(storeList) > 0 { + if len(storeList) == 1 { + qs = qs.Where("store_id = ?", storeList[0]) + } else { + qs = qs.Where("store_id IN (?)", storeList) + } + } else { + return nil, errors.New("用户未绑定门店") + } + } else { + var allStoreList []Store + err := orm.Eloquent.Table("store").Where("is_online = 1 and cooperative_business_id = 1"). + Find(&allStoreList).Error + if err != nil { + return nil, err + } + + for _, v := range allStoreList { + storeList = append(storeList, v.ID) + } + } + + // 限定门店 + if len(req.StoreId) > 0 { + qs = qs.Where("store_id IN ?", req.StoreId) + } + + // 限定时间 + if req.StartTime != "" && req.EndTime != "" { + qs = qs.Where("date BETWEEN ? AND ?", req.StartTime, req.EndTime) + } + + // 查询全部数据(用于聚合、分页) + var allData []DailyBusinessSummary + err := qs.Order("date DESC").Find(&allData).Error + if err != nil { + logger.Error("QueryDailyBusinessSummaryData err:", logger.Field("err", err)) + return nil, err + } + + // 查询汇总数据 + var summary struct { + TotalSalesAmount float64 + TotalPromotionFee float64 + TotalSalesProfit float64 + TotalStaffProfit float64 + TotalCount int64 + } + err = qs.Select("SUM(sales_amount) AS total_sales_amount, " + + "SUM(promotion_fee) AS total_promotion_fee, " + + "SUM(sales_profit) AS total_sales_profit, " + + "SUM(staff_profit) AS total_staff_profit, " + + "SUM(count) AS total_count"). + Find(&summary).Error + if err != nil { + logger.Error("QueryStoreManageData summary err:", logger.Field("err", err)) + return nil, err + } + + // 查询分页数据 按 store_id 进行汇总 + var storeData []struct { + StoreId uint32 `json:"store_id"` + SalesAmount float64 `json:"sales_amount"` + PromotionFee float64 `json:"promotion_fee"` + SalesProfit float64 `json:"sales_profit"` + StaffProfit float64 `json:"staff_profit"` + Count int64 `json:"count"` + } + err = qs.Select("store_id, " + + "SUM(promotion_fee) AS promotion_fee, " + + "SUM(sales_amount) AS total_sales_amount, " + + "SUM(sales_profit) AS sales_profit, " + + "SUM(staff_profit) AS staff_profit, " + + "SUM(count) AS count"). + Group("store_id"). + Order("store_id"). + Find(&storeData).Error + if err != nil { + logger.Error("QueryStoreManageData err:", logger.Field("err", err)) + return nil, err + } + + storeMap, err := GetAllStoreData() + if err != nil { + return nil, err + } + + // 组合数据 + for _, storeId := range storeList { + flag := false + for _, v := range storeData { + if v.StoreId == storeId { + flag = true + storeManageDataList = append(storeManageDataList, MarketStoreSalesData{ + StoreId: v.StoreId, + StoreName: storeMap[v.StoreId].Name, + SalesAmount: math.Round(v.SalesAmount*100) / 100, + PromotionFee: math.Round(v.PromotionFee*100) / 100, + SalesProfit: math.Round(v.SalesProfit*100) / 100, + StaffProfit: math.Round(v.StaffProfit*100) / 100, + Count: v.Count, + }) + } else { + continue + } + } + + if !flag { + storeManageDataList = append(storeManageDataList, MarketStoreSalesData{ + StoreId: storeId, + StoreName: storeMap[storeId].Name, + SalesAmount: 0, + PromotionFee: 0, + SalesProfit: 0, + StaffProfit: 0, + Count: 0, + }) + } + } + + // 汇总数据赋值给响应 + resp.TotalSalesAmount = math.Round(summary.TotalSalesAmount*100) / 100 + resp.TotalPromotionFee = math.Round(summary.TotalPromotionFee*100) / 100 + resp.TotalSalesProfit = math.Round(summary.TotalSalesProfit*100) / 100 + resp.TotalStaffProfit = math.Round(summary.TotalStaffProfit*100) / 100 + resp.TotalCount = summary.TotalCount + + // 是否导出 + if req.IsExport == 1 { + filePath, err := marketStoreSalesDataExport(storeManageDataList, summary, c) + if err != nil { + logger.Error("storeSalesDataExport err:", logger.Field("err", err)) + return nil, err + } + resp.ExportUrl = filePath + } else { + // 分页 + startIndex := page * req.PageSize + endIndex := startIndex + req.PageSize + if endIndex > len(storeManageDataList) { + endIndex = len(storeManageDataList) + } + resp.List = storeManageDataList[startIndex:endIndex] + resp.Total = len(storeManageDataList) + } + + return resp, nil +} + +// marketStoreSalesDataExport 导出门店经营数据 +func marketStoreSalesDataExport(list []MarketStoreSalesData, summary struct { + TotalSalesAmount float64 + TotalPromotionFee float64 + TotalSalesProfit float64 + TotalStaffProfit float64 + TotalCount int64 +}, c *gin.Context) (string, error) { + file := excelize.NewFile() + fSheet := "Sheet1" + + url := config.ExportConfig.Url + fileName := time.Now().Format(TimeFormat) + "门店经营数据" + ".xlsx" + fmt.Println("url fileName:", url+fileName) + + // 判断是否有入销售毛利、员工毛利的权限 + flag1, _ := checkRoleMenu(c, SalesProfitMenu) + flag2, _ := checkRoleMenu(c, StaffProfitMenu) + fmt.Println("flag1 is:", flag1) + fmt.Println("flag2 is:", flag2) + logger.Info("flag1 is:", logger.Field("flag1", flag1)) + logger.Info("flag2 is:", logger.Field("flag2", flag2)) + + nEndCount := 0 + title := []interface{}{"店铺名称", "销售额", "推广费"} + if flag1 { // 销售毛利 + title = append(title, "销售毛利") + nEndCount += 1 + } + if flag2 { // 员工毛利 + title = append(title, "员工毛利") + nEndCount += 1 + } + title = append(title, "销售数量") + + for i, _ := range title { + cell, _ := excelize.CoordinatesToCellName(1+i, 1) + err := file.SetCellValue(fSheet, cell, title[i]) + if err != nil { + logger.Errorf("file set value err:", err) + } + } + + var row []interface{} + nExcelStartRow := 0 + for rowId := 0; rowId < len(list); rowId++ { + row = []interface{}{ + list[rowId].StoreName, + list[rowId].SalesAmount, + list[rowId].PromotionFee, + } + + // 控制是否导出 + if flag1 { // 销售毛利 + row = append(row, list[rowId].SalesProfit) + } + if flag2 { // 员工毛利 + row = append(row, list[rowId].StaffProfit) + } + row = append(row, list[rowId].Count) + + for j, _ := range row { + cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) + err := file.SetCellValue(fSheet, cell, row[j]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + nExcelStartRow++ + } + + totalData := "汇总 记录数:" + strconv.FormatInt(int64(len(list)), 10) + end := []interface{}{totalData, summary.TotalSalesAmount, summary.TotalPromotionFee} + + if flag1 { // 销售毛利 + end = append(end, summary.TotalSalesProfit) + } + if flag2 { // 员工毛利 + end = append(end, summary.TotalStaffProfit) + } + end = append(end, summary.TotalCount) + + for i, _ := range end { + cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) + err := file.SetCellValue(fSheet, cell, end[i]) + if err != nil { + logger.Error("file set value err:", logger.Field("err", err)) + } + } + + // 设置所有单元格的样式: 居中、加边框 + style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, + "border":[{"type":"left","color":"000000","style":1}, + {"type":"top","color":"000000","style":1}, + {"type":"right","color":"000000","style":1}, + {"type":"bottom","color":"000000","style":1}]}`) + + //设置单元格高度 + file.SetRowHeight("Sheet1", 1, 20) + + // 设置单元格大小 + file.SetColWidth("Sheet1", "A", "A", 30) + file.SetColWidth("Sheet1", "B", "B", 18) + file.SetColWidth("Sheet1", "C", "C", 18) + file.SetColWidth("Sheet1", "D", "D", 18) + file.SetColWidth("Sheet1", "E", "E", 18) + file.SetColWidth("Sheet1", "F", "F", 18) + + var endRow string + switch nEndCount { + case 1: + endRow = fmt.Sprintf("E"+"%d", nExcelStartRow+2) + case 2: + endRow = fmt.Sprintf("F"+"%d", nExcelStartRow+2) + default: + endRow = fmt.Sprintf("D"+"%d", nExcelStartRow+2) + } + // 应用样式到整个表格 + _ = file.SetCellStyle("Sheet1", "A1", endRow, style) + + fmt.Println("save fileName:", config.ExportConfig.Path+fileName) + if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { + fmt.Println(err) + } + + return url + fileName, nil +} diff --git a/app/admin/models/game_card.go b/app/admin/models/game_card.go index c46e6af..b40a524 100644 --- a/app/admin/models/game_card.go +++ b/app/admin/models/game_card.go @@ -1561,58 +1561,98 @@ func DeliveryStorePickStatusUpdate() { //} } +// MemberExpirationReminder 给会员即将过期且有卡带未归还的用户发短信提醒续费 func MemberExpirationReminder() { - - //start1 := time.Now().Add(-7 * 24 * time.Hour) - //end1 := start1.AddDate(0, 0, 1) - // - //var users []UserInfo - //err := orm.Eloquent.Table("user").Where("member_expire > ?", start1).Where("member_expire < ?", end1). - // Where("member_level in (?)", []uint32{2, 3, 4, 5}).Find(&users).Error - //if err != nil { - // logger.Error(err.Error().Error()()) - // return - //} - // - //if len(users) == 0 { - // logger.Info("users is null") - // return - //} - // - //content := "【明慧科技】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服" - //for i, _ := range users { - // if users[i].Tel == "" { - // continue - // } - // - // unreturned := &struct { - // Total int `json:"count"` - // }{} - // sql := fmt.Sprintf("SELECT COUNT(*) AS count FROM `order` WHERE uid = %d AND pay_status=2 AND card_status IN (1,2,3) ;", users[i].Uid) - // err := orm.Eloquent.Raw(sql).Scan(unreturned).Error - // if err != nil { - // logger.Error(err.Error().Error()()) - // continue - // } - // fmt.Println("订单数量count:", unreturned.Total) - // if unreturned.Total == 0 { - // continue - // } - // err = SmsSend(users[i].Tel, content) - // if err != nil { - // logger.Error(err.Error()) - // } - //} - list := []int64{7, 4, 0} for i, _ := range list { MemberExpirationReminderDay(list[i]) } - } func MemberExpirationReminderDay(days int64) { + smsSend := &ExpireMemberSmsSend{ + Status: 1, + SmsType: SmsTypeRenewRemind, + } + + start := time.Now().Add(time.Duration(days) * 24 * time.Hour) + end := start.AddDate(0, 0, 1) + + var users []UserInfo + err := orm.Eloquent.Table("user").Where("member_expire > ?", start).Where("member_expire < ?", end). + Where("member_level in (?)", []uint32{2, 3, 4, 5}).Find(&users).Error + if err != nil { + logger.Error(err.Error()) + return + } + + if len(users) == 0 { + logger.Info("users is null") + return + } + + for _, u := range users { + if u.Tel == "" { + continue + } + + // 查询未归还卡带 + var cards []OrderCard + err = orm.Eloquent.Table("order_card"). + Where("uid = ? AND pay_status = 2 AND card_status IN (1,2,3)", u.Uid). + Find(&cards).Error + if err != nil || len(cards) == 0 { + logger.Error("Query order_card failed", logger.Field("err", err)) + continue + } + + // 填充游戏信息 + cards = OrderCardListSetGameInfo(cards) + // 提取游戏名称 + gameNames := make([]string, 0) + for _, card := range cards { + if card.GameName != "" { + gameNames = append(gameNames, card.GameName) + } + } + // 拼接短信内容 + cardNamesStr := strings.Join(gameNames, "、") + + userStore, err := GetUserBelongStore(u.Uid, u.StoreId) + if err != nil { + smsSend.Message = fmt.Sprintf("【明慧科技】温馨提示:您的go2ns租卡会员还有%d天将到期,如卡带到期未归将产生滞纳金,您可将游戏卡(%s)邮寄/到店归还,更多续约优惠可联系咨询~", + days, cardNamesStr) + } else { + smsSend.Message = fmt.Sprintf("【明慧科技】温馨提示:您的go2ns租卡会员还有%d天将到期,如卡带到期未归将产生滞纳金,您可将游戏卡(%s)邮寄/到店归还,更多续约优惠可联系咨询~(%s %s)", + days, cardNamesStr, userStore.Name, userStore.Landline) + } + smsSend.Tel = u.Tel + smsSend.SendTime = time.Now() + + // 发送短信 + err = GtSendMessage([]string{u.Tel}, smsSend.Message) + if err != nil { + smsSend.Status = 2 + logger.Error("sms send failed", logger.Field("err", err)) + } + + // 存记录 + err = orm.Eloquent.Create(&smsSend).Error + if err != nil { + logger.Error("create expire member sms send err:", logger.Field("err", err)) + } + } +} + +func MemberExpirationReminderDayOld(days int64) { + smsSend := &ExpireMemberSmsSend{ + SendTime: time.Now(), + Tel: "", + Status: 1, + SmsType: SmsTypeRenewRemind, + } + start := time.Now().Add(time.Duration(days) * 24 * time.Hour) end := start.AddDate(0, 0, 1) @@ -1639,7 +1679,7 @@ func MemberExpirationReminderDay(days int64) { Count int `json:"count"` }{} sql := fmt.Sprintf("SELECT COUNT(*) AS count FROM order_card WHERE uid = %d AND pay_status=2 AND card_status IN (1,2,3) ;", users[i].Uid) - err := orm.Eloquent.Raw(sql).Scan(unreturned).Error + err = orm.Eloquent.Raw(sql).Scan(unreturned).Error if err != nil { logger.Error(err.Error()) continue @@ -1651,8 +1691,17 @@ func MemberExpirationReminderDay(days int64) { //fmt.Println("content:", content) err = GtSendMessage([]string{users[i].Tel}, content) if err != nil { + smsSend.Status = 2 logger.Error(err.Error()) } + + // 存记录 + smsSend.Tel = users[i].Tel + smsSend.Message = content + err = orm.Eloquent.Create(&smsSend).Error + if err != nil { + logger.Error("create expire member sms send err:", logger.Field("err", err)) + } } } @@ -1666,6 +1715,76 @@ func ExpireMemberSMSSend() { } func ExpireMemberSMSSendDay(day uint32, nowTime time.Time) { + smsSend := &ExpireMemberSmsSend{ + Status: 1, + SmsType: SmsTypeReturnCard, + } + + start := nowTime.AddDate(0, 0, int(day)*(-1)) + end := start.AddDate(0, 0, 1) + + var users []UserInfo + err := orm.Eloquent.Table("user").Where("member_expire > ?", start).Where("member_expire < ?", end). + Where("member_level = ?", MemberLevelUser).Find(&users).Error + if err != nil { + logger.Error(err.Error()) + return + } + + if len(users) == 0 { + logger.Info("users is null") + return + } + + for _, user := range users { + if user.Tel == "" { + continue + } + + // 查询未归还卡带 + var cards []OrderCard + err = orm.Eloquent.Table("order_card"). + Where("uid = ? AND pay_status = 2 AND card_status IN (1,2,3)", user.Uid). + Find(&cards).Error + if err != nil || len(cards) == 0 { + logger.Error("Query order_card failed", logger.Field("err", err)) + continue + } + + // 填充游戏信息 + cards = OrderCardListSetGameInfo(cards) + + // 提取游戏名称 + gameNames := make([]string, 0) + for _, card := range cards { + if card.GameName != "" { + gameNames = append(gameNames, card.GameName) + } + } + + // 拼接短信内容 + cardNamesStr := strings.Join(gameNames, "、") + smsSend.Message = fmt.Sprintf("【明慧科技】您的go2ns租卡会员已过期%d天,卡带(%s)未归还产生滞纳金%d元,请及时续费会员或归还卡带,以避免对您造成不必要的损失。", day, cardNamesStr, day*2) + smsSend.Tel = user.Tel + smsSend.SendTime = time.Now() + + // 发送短信 + err = GtSendMessage([]string{user.Tel}, smsSend.Message) + if err != nil { + smsSend.Status = 2 + logger.Error("sms send failed", logger.Field("err", err)) + } + + // 存记录 + err = orm.Eloquent.Create(&smsSend).Error + if err != nil { + logger.Error("create expire member sms send err:", logger.Field("err", err)) + } + } +} + +// ExpireMemberSMSSendDayOld 老版本 +func ExpireMemberSMSSendDayOld(day uint32, nowTime time.Time) { smsSend := &ExpireMemberSmsSend{ Message: fmt.Sprintf("【明慧科技】您的go2ns租卡会员已过期%d天,卡带未归还产生滞纳金%d元,请及时续费会员或归还卡带,以避免对您造成不必要的损失。", day, day*2), SendTime: nowTime, @@ -1694,6 +1813,7 @@ func ExpireMemberSMSSendDay(day uint32, nowTime time.Time) { if users[i].Tel == "" { continue } + smsSend.Tel = users[i].Tel exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM order_card WHERE uid = %d AND pay_status=2 AND card_status IN (1,2,3) ", users[i].Uid)) if err != nil || !exist { diff --git a/app/admin/models/repair.go b/app/admin/models/repair.go index 164a6de..55bb85f 100644 --- a/app/admin/models/repair.go +++ b/app/admin/models/repair.go @@ -4,9 +4,11 @@ import ( "errors" "fmt" "github.com/gin-gonic/gin" + "github.com/xuri/excelize/v2" orm "go-admin/common/global" "go-admin/logger" "go-admin/tools" + "go-admin/tools/config" "gorm.io/gorm" "math/rand" "time" @@ -39,7 +41,9 @@ type RepairRecord struct { AuditTime *time.Time `json:"audit_time"` // 审核时间 SendTime *time.Time `json:"send_time"` // 送修时间 SendRemark string `json:"send_remark"` // 送修备注 + ExpressType uint8 `json:"express_type"` // 邮寄方:1-个人;2-门店 ExpressNo string `json:"express_no"` // 快递单号 + ExpressFee float64 `json:"express_fee"` // 快递费用 RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID(供应商) RepairUnitName string `json:"repair_unit_name" gorm:"-"` // 维修单位名称(供应商) RepairFee float64 `json:"repair_fee"` // 维修费用 @@ -51,14 +55,14 @@ type RepairRecord struct { type RepairRecordCommodity struct { Model - RepairRecordId uint32 `json:"repair_record_id"` // 维修记录ID - CommodityId uint32 `json:"commodity_id"` // 商品ID - CommodityName string `json:"commodity_name" gorm:"-"` // 商品名称 - OldIMEI string `json:"old_imei"` // 旧机器编码 - NewIMEI string `json:"new_imei"` // 新机器编码 - Count uint32 `json:"count"` // 数量 - FaultImage string `json:"fault_image"` // 故障图片 - FaultDesc string `json:"fault_desc"` // 故障描述 + RepairRecordId uint32 `json:"repair_record_id"` // 维修记录ID + CommodityId uint32 `json:"commodity_id"` // 商品ID + CommodityName string `json:"commodity_name"` // 商品名称 + OldIMEI string `json:"old_imei"` // 旧机器编码 + NewIMEI string `json:"new_imei"` // 新机器编码 + Count uint32 `json:"count"` // 数量 + FaultImage string `json:"fault_image"` // 故障图片 + FaultDesc string `json:"fault_desc"` // 故障描述 } type RepairCreateReq struct { @@ -109,6 +113,7 @@ type ErpRepairOrderListReq struct { StoreId []uint32 `json:"store_id"` // 门店id HandlerId uint32 `json:"handler_id"` // 经手人id State []uint32 `json:"state"` // 状态:1-待审核,2-已审核,3-维修中,4-已完成 + RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID(供应商) MakeTimeStart string `json:"make_time_start"` // 制单开始时间 MakeTimeEnd string `json:"make_time_end"` // 制单结束时间 AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 @@ -142,10 +147,12 @@ type ErpRepairAuditReq struct { // ErpRepairSendReq 维修订单送修入参 type ErpRepairSendReq struct { - SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 - RepairUnitId uint32 `json:"repair_unit_id" binding:"required"` // 维修单位ID(供应商) - ExpressNo string `json:"express_no"` // 快递单号 - SendRemark string `json:"send_remark"` // 送修备注 + SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 + RepairUnitId uint32 `json:"repair_unit_id" binding:"required"` // 维修单位ID(供应商) + ExpressType uint8 `json:"express_type" binding:"required"` // 邮寄方:1-个人;2-门店 + ExpressNo string `json:"express_no"` // 快递单号 + ExpressFee float64 `json:"express_fee"` // 快递费用 + SendRemark string `json:"send_remark"` // 送修备注 } // ErpRepairCompleteReq 维修订单完成入参 @@ -336,7 +343,7 @@ func updateRepairCommodityData(gdb *gorm.DB, orderId uint32, req *RepairEditReq) return nil } -// List 查询采购订单列表 +// List 查询维修订单列表 func (m *ErpRepairOrderListReq) List(c *gin.Context) (*ErpRepairOrderListResp, error) { resp := &ErpRepairOrderListResp{ PageIndex: m.PageIndex, @@ -357,6 +364,9 @@ func (m *ErpRepairOrderListReq) List(c *gin.Context) (*ErpRepairOrderListResp, e } else if m.Tel != "" { qs = qs.Where("tel = ?", m.Tel) } else { + if m.RepairUnitId != 0 { + qs = qs.Where("repair_unit_id = ?", m.RepairUnitId) + } if len(m.StoreId) != 0 { qs = qs.Where("store_id in ?", m.StoreId) } @@ -524,7 +534,9 @@ func SendToRepair(req *ErpRepairSendReq, c *gin.Context) error { nowTime := time.Now() repairRecord.RepairUnitId = req.RepairUnitId + repairRecord.ExpressType = req.ExpressType repairRecord.ExpressNo = req.ExpressNo + repairRecord.ExpressFee = req.ExpressFee repairRecord.SendRemark = req.SendRemark repairRecord.State = ErpRepairOrderBingRepaired repairRecord.SendTime = &nowTime @@ -584,3 +596,362 @@ func CompleteRepairOrder(req *ErpRepairCompleteReq, c *gin.Context) error { return nil } + +// ErpRepairDetailReportReq 维修明细汇总入参 +type ErpRepairDetailReportReq struct { + SerialNumber string `json:"serial_number"` // 单据编号 + Uid uint32 `json:"uid"` // 用户ID + Tel string `json:"tel"` // 手机号 + MemberLevel uint32 `json:"member_level"` // 会员等级:普通用户、尊享会员 + StoreId []uint32 `json:"store_id"` // 门店id + HandlerId uint32 `json:"handler_id"` // 经手人id + RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID(供应商) + MakeTimeStart string `json:"make_time_start"` // 制单开始时间 + MakeTimeEnd string `json:"make_time_end"` // 制单结束时间 + AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 + AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间 + SendTimeStart string `json:"send_time_start"` // 送修开始时间 + SendTimeEnd string `json:"send_time_end"` // 送修结束时间 + CompleteTimeStart string `json:"complete_time_start"` // 维修完成开始时间 + CompleteTimeEnd string `json:"complete_time_end"` // 维修完成结束时间 + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 页面条数 + IsExport uint32 `json:"is_export"` // 1-导出 + //State []uint32 `json:"state"` // 状态:1-待审核,2-已审核,3-维修中,4-已完成 +} + +// ErpRepairDetailReportResp 维修明细汇总出参 +type ErpRepairDetailReportResp struct { + Total int `json:"total"` // 总条数(总订单数) + PageIndex int `json:"pageIndex"` // 页码 + PageSize int `json:"pageSize"` // 每页展示条数 + ExportUrl string `json:"export_url"` // 导出excel地址 + List []RepairRecord `json:"list"` // 零售明细 + SumData RepairDetailTotalData `json:"sumData"` // 汇总数据 +} + +// RepairDetailTotalData 维修明细相关金额汇总 +type RepairDetailTotalData struct { + Count int32 `json:"count"` // 维修数量 + TotalExpressFee float64 `json:"total_express_fee"` // 快递费用 + TotalRepairFee float64 `json:"total_repair_fee"` // 维修费用 +} + +// RepairDetailReport 维修明细 +func RepairDetailReport(req *ErpRepairDetailReportReq, c *gin.Context) (*ErpRepairDetailReportResp, error) { + page := req.PageIndex - 1 + if page < 0 { + page = 0 + } + if req.PageSize == 0 { + req.PageSize = 10 + } + + db := orm.Eloquent.Table("repair_record AS rr"). + Select("rr.*, s.name AS store_name, ru.name AS repair_unit_name"). + Joins("LEFT JOIN store s ON rr.store_id = s.id"). + Joins("LEFT JOIN erp_supplier ru ON rr.repair_unit_id = ru.id"). + Where("state in (3,4)") + // 条件过滤 + db = buildRepairDetailQuery(db, req) + + sumQuery := orm.Eloquent.Table("repair_record AS rr"). + Select("COUNT(*) AS count, COALESCE(SUM(rr.express_fee), 0) AS total_express_fee, COALESCE(SUM(rr.repair_fee), 0) AS total_repair_fee"). + Where("state in (3,4)") + sumQuery = buildRepairDetailQuery(sumQuery, req) + + // 非管理员才判断所属门店 + if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { + sysUser, err := GetSysUserByCtx(c) + if err != nil { + return nil, errors.New("操作失败:" + err.Error()) + } + + // 返回sysUser未过期的门店id列表 + storeList := GetValidStoreIDs(sysUser.StoreData) + if len(storeList) > 0 { + if len(storeList) == 1 { + db = db.Where("store_id = ?", storeList[0]) + sumQuery = sumQuery.Where("store_id = ?", storeList[0]) + } else { + db = db.Where("store_id IN (?)", storeList) + sumQuery = sumQuery.Where("store_id IN (?)", storeList) + } + } else { + return nil, errors.New("用户未绑定门店") + } + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + return nil, err + } + + var records []RepairRecord + query := db.Order("rr.id DESC") + + if req.IsExport != 1 { + query = query.Offset(page * req.PageSize).Limit(req.PageSize) + } + + if err := query.Find(&records).Error; err != nil { + return nil, err + } + + // 查询商品明细 + for i := range records { + var commodities []RepairRecordCommodity + err := orm.Eloquent.Table("repair_record_commodity AS rc"). + Select("rc.*, ec.name AS commodity_name"). + Joins("LEFT JOIN erp_commodity ec ON rc.commodity_id = ec.id"). + Where("rc.repair_record_id = ?", records[i].ID). + Find(&commodities).Error + if err != nil { + return nil, err + } + records[i].Commodities = commodities + } + + // 汇总数据 + var sumData RepairDetailTotalData + if err := sumQuery.Scan(&sumData).Error; err != nil { + return nil, err + } + + resp := &ErpRepairDetailReportResp{ + Total: int(total), + PageIndex: req.PageIndex, + PageSize: req.PageSize, + List: records, + SumData: sumData, + } + + // 处理导出逻辑 + if req.IsExport == 1 { + exportUrl, err := ExportRepairDetailExcel(records, sumData) + if err != nil { + return nil, err + } + resp.ExportUrl = exportUrl + resp.List = nil + + } + + return resp, nil +} + +func buildRepairDetailQuery(base *gorm.DB, req *ErpRepairDetailReportReq) *gorm.DB { + if req.SerialNumber != "" { + base = base.Where("rr.serial_number = ?", req.SerialNumber) + } + if req.Uid > 0 { + base = base.Where("rr.uid = ?", req.Uid) + } + if req.Tel != "" { + base = base.Where("rr.tel = ?", req.Tel) + } + if req.MemberLevel > 0 { + base = base.Where("rr.member_level = ?", req.MemberLevel) + } + if len(req.StoreId) > 0 { + base = base.Where("rr.store_id IN (?)", req.StoreId) + } + if req.HandlerId > 0 { + base = base.Where("rr.handler_id = ?", req.HandlerId) + } + //if len(req.State) > 0 { + // base = base.Where("rr.state IN (?)", req.State) + //} + if req.RepairUnitId > 0 { + base = base.Where("rr.repair_unit_id = ?", req.RepairUnitId) + } + if req.MakeTimeStart != "" && req.MakeTimeEnd != "" { + base = base.Where("rr.maker_time BETWEEN ? AND ?", req.MakeTimeStart, req.MakeTimeEnd) + } + if req.AuditTimeStart != "" && req.AuditTimeEnd != "" { + base = base.Where("rr.audit_time BETWEEN ? AND ?", req.AuditTimeStart, req.AuditTimeEnd) + } + if req.SendTimeStart != "" && req.SendTimeEnd != "" { + base = base.Where("rr.send_time BETWEEN ? AND ?", req.SendTimeStart, req.SendTimeEnd) + } + if req.CompleteTimeStart != "" && req.CompleteTimeEnd != "" { + base = base.Where("rr.completed_time BETWEEN ? AND ?", req.CompleteTimeStart, req.CompleteTimeEnd) + } + return base +} + +// ExportRepairDetailExcel 导出维修明细报表excel +func ExportRepairDetailExcel(list []RepairRecord, sumData RepairDetailTotalData) (string, error) { + file := excelize.NewFile() + sheet := "Sheet1" + + url := config.ExportConfig.Url + fileName := time.Now().Format(TimeFormat) + "维修明细.xlsx" + savePath := config.ExportConfig.Path + fileName + fmt.Println("生成路径:", url+fileName) + + storeMap, err := GetAllStoreData() + if err != nil { + return "", err + } + + // 表头 + title := []interface{}{ + "维修单号", "门店", "用户ID", "联系电话", "会员等级", "状态", "制单人", "制单时间", "审核人", "审核时间", + "送修时间", "送修备注", "邮寄方式", "快递单号", "快递费用", "维修单位", "维修费用", "完成时间", "完成备注", + "商品名称", "旧机器编码", "新机器编码", "数量", "故障描述", + } + + // 写表头 + for i, v := range title { + cell, _ := excelize.CoordinatesToCellName(i+1, 1) + _ = file.SetCellValue(sheet, cell, v) + } + + rowIndex := 2 + for _, record := range list { + memberLevelStr := GetUserTypeString(int(record.MemberLevel)) + + stateMap := map[uint8]string{ + 1: "待审核", + 2: "待送修", + 3: "维修中", + 4: "已完成", + } + stateStr := stateMap[record.State] + + sendTypeStr := map[uint8]string{ + 1: "个人", + 2: "门店", + }[record.ExpressType] + + nFlag := 0 + for _, comm := range record.Commodities { + var row []interface{} + if nFlag >= 1 { + row = []interface{}{ + record.SerialNumber, + storeMap[record.StoreId].Name, + record.Uid, + record.Tel, + memberLevelStr, + stateStr, + record.MakerName, + formatDateTime(record.MakerTime), + record.AuditorName, + formatTime(record.AuditTime), + formatTime(record.SendTime), + record.SendRemark, + sendTypeStr, + record.ExpressNo, + "", + record.RepairUnitName, + "", + formatTime(record.CompletedTime), + record.CompleteRemark, + comm.CommodityName, + comm.OldIMEI, + comm.NewIMEI, + comm.Count, + comm.FaultDesc, + } + } else { + row = []interface{}{ + record.SerialNumber, + storeMap[record.StoreId].Name, + record.Uid, + record.Tel, + memberLevelStr, + stateStr, + record.MakerName, + formatDateTime(record.MakerTime), + record.AuditorName, + formatTime(record.AuditTime), + formatTime(record.SendTime), + record.SendRemark, + sendTypeStr, + record.ExpressNo, + record.ExpressFee, + record.RepairUnitName, + record.RepairFee, + formatTime(record.CompletedTime), + record.CompleteRemark, + comm.CommodityName, + comm.OldIMEI, + comm.NewIMEI, + comm.Count, + comm.FaultDesc, + } + } + + for j, val := range row { + cell, _ := excelize.CoordinatesToCellName(j+1, rowIndex) + _ = file.SetCellValue(sheet, cell, val) + } + rowIndex++ + nFlag++ + } + } + + // 汇总行 + summary := []interface{}{ + fmt.Sprintf("维修单数: %d", len(list)), + "", "", "", "", "", "", "", "", "", "", "", "", "", sumData.TotalExpressFee, "", sumData.TotalRepairFee, + } + + for i, val := range summary { + cell, _ := excelize.CoordinatesToCellName(i+1, rowIndex) + _ = file.SetCellValue(sheet, cell, val) + } + + // 设置样式 + style, _ := file.NewStyle(`{ + "alignment":{"horizontal":"center","vertical":"center"}, + "border":[ + {"type":"left","color":"000000","style":1}, + {"type":"top","color":"000000","style":1}, + {"type":"right","color":"000000","style":1}, + {"type":"bottom","color":"000000","style":1} + ] + }`) + + lastCol, _ := excelize.ColumnNumberToName(len(title)) + endCell := fmt.Sprintf("%s%d", lastCol, rowIndex) + _ = file.SetCellStyle(sheet, "A1", endCell, style) + + // 设置列宽(根据字段名称微调) + file.SetColWidth(sheet, "A", "A", 18) + file.SetColWidth(sheet, "B", "B", 25) + file.SetColWidth(sheet, "C", "C", 10) + file.SetColWidth(sheet, "D", "D", 15) + file.SetColWidth(sheet, "E", "E", 15) + file.SetColWidth(sheet, "J", "J", 18) + file.SetColWidth(sheet, "K", "K", 18) + file.SetColWidth(sheet, "N", "N", 20) + file.SetColWidth(sheet, "S", "S", 18) + file.SetColWidth(sheet, "T", "T", 25) + file.SetColWidth(sheet, "X", "X", 30) + + // 保存 + if err := file.SaveAs(savePath); err != nil { + logger.Error("保存文件失败:", logger.Field("err", err)) + return "", err + } + return url + fileName, nil +} + +// formatTime 格式化时间为空判断 +func formatTime(t *time.Time) string { + if t == nil { + return "" + } + return t.Format("2006-01-02 15:04:05") +} + +// formatDateTime 格式化时间为空判断(仅保留日期) +func formatDateTime(t *time.Time) string { + if t == nil { + return "" + } + return t.Format("2006-01-02") +} diff --git a/app/admin/models/store.go b/app/admin/models/store.go index 873685b..2e3cfe3 100644 --- a/app/admin/models/store.go +++ b/app/admin/models/store.go @@ -12,7 +12,8 @@ type Store struct { Name string `json:"name"` // 门店名称 Img string `json:"img"` // 门面图 - Tel string `json:"tel" gorm:"type:varchar(20)"` // 电话 + Tel string `json:"tel" gorm:"type:varchar(20)"` // 电话(手机号) + Landline string `json:"landline" gorm:"type:varchar(20)"` // 固定电话 Province string `json:"province" gorm:"type:varchar(100)"` // 省 City string `json:"city" gorm:"type:varchar(100)"` // 市 District string `json:"district" gorm:"type:varchar(100)"` // 区 @@ -276,6 +277,9 @@ func (m *Store) getModifyPara() map[string]interface{} { if m.MemberService != 0 { paraMap["member_service"] = m.MemberService } + if m.Landline != "" { + paraMap["landline"] = m.Landline + } paraMap["sales_comm_rate"] = m.SalesCommRate diff --git a/app/admin/models/user.go b/app/admin/models/user.go index 83fe1b5..b6c0741 100644 --- a/app/admin/models/user.go +++ b/app/admin/models/user.go @@ -73,6 +73,12 @@ const ( // 尊享会员延保状态 ExtendEffectivity = 3 // 已激活 ) +const ( // 短信记录类型 + SmsTypeRenewCoupon = 1 // 续费优惠提醒 + SmsTypeRenewRemind = 2 // 续费提醒 + SmsTypeReturnCard = 3 // 还卡提醒 +) + // gen:qs // //go:generate goqueryset -in user.go @@ -1006,13 +1012,15 @@ func (*GroupSendMessageTemplate) TableName() string { return "group_send_message_template" } +// ExpireMemberSmsSend SmsType:1-会员剩余1个月续费提醒;2-会员剩余7天/4天/0天(且有卡带未归还/游玩中);3-过期未还卡提醒(最多会发12条,对应天数:1, 2, 3, 4, 5, 6, 7, 14, 21, 28, 60, 90) type ExpireMemberSmsSend struct { Model Message string `json:"message"` SendTime time.Time `json:"send_time"` Tel string `json:"tel" gorm:"index"` - Status uint32 `json:"status"` // 1-发送成功 2-发送失败 + Status uint32 `json:"status"` // 1-发送成功 2-发送失败 + SmsType uint32 `json:"sms_type"` // 1-续费优惠;2-续费;3-还卡 } func GetUserInfoMap(uids []uint32) (map[uint32]UserInfo, error) { @@ -3515,19 +3523,94 @@ func (m *OperationLog) AddLog() { } func SendMessageMemberRenewal() { - - //user.MemberExpire.Before(utils.Now().AddDate(0, 1, 0)) - //start := TodayZero().AddDate(0, 1, 0) - ////end := start.AddDate(0, 0, 1) - //end := start - //fmt.Println("end:", end.Format(TimeFormat)) - //var users []UserInfo - //err := orm.Eloquent.Table("user").Where("member_level IN (2,4,5)").Where("member_expire < ?", end).Find(&users).Error - //if err != nil { - // logger.Error("cooperative err:",logger.Field("err",err)) - // return - //} fmt.Println("发送续费短息开始:") + smsSend := &ExpireMemberSmsSend{ + Status: 1, + SmsType: SmsTypeRenewCoupon, + } + + start := TodayZero().AddDate(0, 0, GetActivityRenewalConfig().PopTrap) + end := start.AddDate(0, 0, 1) + + var users []UserInfo + err := orm.Eloquent.Table("user").Where("member_level IN (2,4,5)"). + Where("member_expire>?", start).Where("member_expire 0 { - message := "【明慧科技】提醒:您的go2ns租卡会员时长仅剩余一个月,现在续费最高立减200元!赶快进入小程序领取优惠吧~" + message := "【明慧科技】提醒:您的go2ns租卡会员时长仅剩余一个月,现在续费最高立减200元!赶快进入小程序领取优惠吧!~更多续约优惠也可联系咨询哦~" err = GtSendMessage(tels, message) if err != nil { logger.Errorf("SmsSend err:", logger.Field("err", err)) @@ -3582,6 +3665,14 @@ func SendMessageMemberRenewal() { if err != nil { logger.Error("create renewal log err:", logger.Field("err", err)) } + + // 存记录 + smsSend.Tel = users[i].Tel + smsSend.Message = message + err = orm.Eloquent.Create(&smsSend).Error + if err != nil { + logger.Error("create expire member sms send err:", logger.Field("err", err)) + } } } fmt.Println("发送续费短息完成...") @@ -3851,10 +3942,9 @@ func UserInviteRecordListSetAssistantName(records []UserInviteRecord) { } type ExpireMemberSmsSendListReq struct { - Status uint32 `json:"status"` // 1-待发送 2-已发送 - Tel string `json:"tel"` - //StartTime string `json:"start_time"` // 开始时间 - //EndTime string `json:"end_time"` // 结束时间 + Status uint32 `json:"status"` // 1-待发送 2-已发送 + Tel string `json:"tel"` // 用户手机号 + SmsType uint32 `json:"sms_type"` // 1-续费优惠;2-续费;3-还卡 StartTime time.Time `json:"start_time"` // 开始时间 EndTime time.Time `json:"end_time"` // 结束时间 PageIndex int `json:"pageIndex"` @@ -3879,23 +3969,9 @@ func (m *ExpireMemberSmsSendListReq) List() (*ExpireMemberSmsSendListResp, error if m.Tel != "" { qs = qs.Where("tel LIKE '%" + m.Tel + "%'") } - //if m.StartTime != "" { - // parse, err := time.Parse(DateTimeFormat, m.StartTime) - // if err != nil { - // logger.Errorf("err:",logger.Field("err",err)) - // return resp, err - // } - // qs = qs.Where("send_time > ?", parse) - //} - //if m.EndTime != "" { - // parse, err := time.Parse(DateTimeFormat, m.EndTime) - // if err != nil { - // logger.Errorf("err:",logger.Field("err",err)) - // return resp, err - // } - // parse = parse.AddDate(0, 0, 1) - // qs = qs.Where("send_time < ?", parse) - //} + if m.SmsType != 0 { + qs = qs.Where("sms_type = ?", m.SmsType) + } if !m.StartTime.IsZero() { qs = qs.Where("send_time > ?", m.StartTime) } @@ -4767,3 +4843,43 @@ func IsValidPrivilegeMember(uid uint32) bool { return false } + +// GetUserBelongStore 查询会员的归属门店 +// 规则:门店正常营业未闭店;优先返回会员归属门店;如果归属门店为空,则返回最近借卡门店 +func GetUserBelongStore(uid uint32, storeId uint64) (*Store, error) { + if uid == 0 { + return nil, errors.New("用户uid为空") + } + + // 如果用户有归属门店,则优先返回(需判断门店是否营业) + if storeId != 0 { + var store Store + err := orm.Eloquent.Table("store"). + Where("id = ? AND is_online = 1", storeId). + First(&store).Error + if err == nil && store.ID != 0 { + return &store, nil + } + } + + // 没有归属门店,查询最近一次借卡门店(按 delivery_time 降序) + var recentCard OrderCard + err := orm.Eloquent.Table("order_card"). + Where("uid = ? AND pay_status = 2 AND card_status IN (1,2,3)", uid). + Order("delivery_time DESC"). + First(&recentCard).Error + if err != nil || recentCard.ID == 0 { + return nil, fmt.Errorf("无归属门店,且无法获取借卡记录: %v", err) + } + + // 查询该借卡记录对应的门店信息 + var recentStore Store + err = orm.Eloquent.Table("store"). + Where("id = ? AND is_online = 1", recentCard.StoreId). + First(&recentStore).Error + if err != nil || recentStore.ID == 0 { + return nil, fmt.Errorf("最近借卡门店已闭店或不存在") + } + + return &recentStore, nil +} diff --git a/app/admin/router/erpordermanage.go b/app/admin/router/erpordermanage.go index 93508ed..7ede836 100644 --- a/app/admin/router/erpordermanage.go +++ b/app/admin/router/erpordermanage.go @@ -26,4 +26,8 @@ func registerErpOrderManageRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJW r.POST("daily_report", erpordermanage.ErpOrderDailyReport) // 经营日报表 r.POST("sale_detail", erpordermanage.ErpOrderSaleDetail) // 查询销售明细 r.POST("import", erpordermanage.ErpOrderBatchImport) // 批量导入零售数据 + + r.POST("business/create", erpordermanage.CreateOrUpdateBusinessSummary) // 创建或更新每日经营数据 + r.POST("business/list", erpordermanage.ListBusinessSummary) // 获取每日经营数据列表 + r.POST("business/summary_data", erpordermanage.ListStoreManageData) // 查询门店经营汇总数据 } diff --git a/app/admin/router/repair.go b/app/admin/router/repair.go index 06dff13..a3e9dd7 100644 --- a/app/admin/router/repair.go +++ b/app/admin/router/repair.go @@ -9,12 +9,13 @@ import ( func registerRepairRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { r := v1.Group("/repair").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) - r.POST("create", repair.CreateRepair) // 新增维修订单 - r.POST("edit", repair.EditRepair) // 编辑 - r.POST("delete", repair.DeleteRepair) // 删除 - r.POST("list", repair.OrderList) // 查询列表 - r.POST("detail", repair.OrderDetail) // 查询列表详情 - r.POST("audit", repair.AuditRepair) // 审核 - r.POST("send", repair.SendRepair) // 送修 - r.POST("complete", repair.CompleteRepair) // 完成 + r.POST("create", repair.CreateRepair) // 新增维修订单 + r.POST("edit", repair.EditRepair) // 编辑 + r.POST("delete", repair.DeleteRepair) // 删除 + r.POST("list", repair.OrderList) // 查询列表 + r.POST("detail", repair.OrderDetail) // 查询列表详情 + r.POST("audit", repair.AuditRepair) // 审核 + r.POST("send", repair.SendRepair) // 送修 + r.POST("complete", repair.CompleteRepair) // 完成 + r.POST("detail_report", repair.OrderDetailReport) // 维修明细 } diff --git a/cmd/api/server.go b/cmd/api/server.go index 760c3d0..87f6f2f 100644 --- a/cmd/api/server.go +++ b/cmd/api/server.go @@ -184,19 +184,19 @@ func run() error { } if config.ApplicationConfig.Mode != "dev" { + // 给会员即将过期且有卡带未归还的用户发短信提醒续费 err = s.Every(1).Day().At("10:30").Do(models.MemberExpirationReminder) - //err = s.Every(1).Minute().Do(models.MemberExpirationReminder) if err != nil { fmt.Println("err:", err) } + // 给会员过期且有卡带未归还的用户发短信提醒还卡 err = s.Every(1).Day().At("12:01").Do(models.ExpireMemberSMSSend) - //err = s.Every(1).Minute().Do(models.MemberExpirationReminder) if err != nil { fmt.Println("err:", err) } - // 用户续费 + // 用户会员到期前一个月发短信提醒用户领取优惠券续费 err = s.Every(1).Day().At("19:00").Do(models.SendMessageMemberRenewal) if err != nil { fmt.Println("err:", err) diff --git a/docs/docs.go b/docs/docs.go index c071906..7bab0d2 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2031,6 +2031,105 @@ const docTemplate = `{ } } }, + "/api/v1/erp_order/business/create": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "商场检查" + ], + "summary": "创建或更新每日经营数据", + "parameters": [ + { + "description": "每日经营数据模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateBusinessSummaryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/app.Response" + } + } + } + } + }, + "/api/v1/erp_order/business/list": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "商场检查" + ], + "summary": "获取每日经营数据列表", + "parameters": [ + { + "description": "获取每日经营数据列表请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.BusinessSummaryListRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BusinessSummaryListResp" + } + } + } + } + }, + "/api/v1/erp_order/business/summary_data": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "商场检查" + ], + "summary": "查询门店经营汇总数据", + "parameters": [ + { + "description": "查询门店经营数据模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.MarketStoreSalesDataReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.MarketStoreSalesDataResp" + } + } + } + } + }, "/api/v1/erp_order/create": { "post": { "consumes": [ @@ -6085,6 +6184,39 @@ const docTemplate = `{ } } }, + "/api/v1/repair/detail_report": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "维修管理,V1.4.6" + ], + "summary": "维修明细", + "parameters": [ + { + "description": "维修明细模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ErpRepairDetailReportReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ErpRepairDetailReportResp" + } + } + } + } + }, "/api/v1/repair/edit": { "post": { "consumes": [ @@ -8139,6 +8271,103 @@ const docTemplate = `{ } } }, + "models.BusinessSummaryItem": { + "type": "object", + "properties": { + "count": { + "description": "销售数量", + "type": "integer" + }, + "date": { + "description": "时间,如:\"2023-12-25\"", + "type": "string" + }, + "promotion_fee": { + "description": "推广费", + "type": "number" + }, + "sales_amount": { + "description": "销售额", + "type": "number" + }, + "sales_profit": { + "description": "销售毛利", + "type": "number" + }, + "staff_profit": { + "description": "员工毛利", + "type": "number" + } + } + }, + "models.BusinessSummaryListRequest": { + "type": "object", + "properties": { + "end_time": { + "description": "结束时间", + "type": "string" + }, + "page_index": { + "description": "当前页码", + "type": "integer" + }, + "page_size": { + "description": "每页条数", + "type": "integer" + }, + "start_time": { + "description": "开始时间", + "type": "string" + }, + "store_id": { + "description": "可选,复选门店ID", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "models.BusinessSummaryListResp": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/models.BusinessSummaryItem" + } + }, + "pageIndex": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "total_count": { + "description": "总销售数量", + "type": "integer" + }, + "total_promotion_fee": { + "description": "总推广费", + "type": "number" + }, + "total_sales_amount": { + "description": "总销售额", + "type": "number" + }, + "total_sales_profit": { + "description": "总销售毛利", + "type": "number" + }, + "total_staff_profit": { + "description": "总员工毛利", + "type": "number" + } + } + }, "models.CancelPrivilegeMembersReq": { "type": "object", "required": [ @@ -9167,6 +9396,43 @@ const docTemplate = `{ } } }, + "models.CreateBusinessSummaryRequest": { + "type": "object", + "required": [ + "date", + "store_id" + ], + "properties": { + "count": { + "description": "销售数量", + "type": "integer" + }, + "date": { + "description": "格式:yyyy-MM-dd", + "type": "string" + }, + "promotion_fee": { + "description": "推广费", + "type": "number" + }, + "sales_amount": { + "description": "销售额", + "type": "number" + }, + "sales_profit": { + "description": "销售毛利", + "type": "number" + }, + "staff_profit": { + "description": "员工毛利", + "type": "number" + }, + "store_id": { + "description": "门店id", + "type": "integer" + } + } + }, "models.CreateErpPurchaseDemandReq": { "type": "object", "properties": { @@ -13971,6 +14237,122 @@ const docTemplate = `{ } } }, + "models.ErpRepairDetailReportReq": { + "type": "object", + "properties": { + "audit_time_end": { + "description": "审核结束时间", + "type": "string" + }, + "audit_time_start": { + "description": "审核开始时间", + "type": "string" + }, + "complete_time_end": { + "description": "维修完成结束时间", + "type": "string" + }, + "complete_time_start": { + "description": "维修完成开始时间", + "type": "string" + }, + "handler_id": { + "description": "经手人id", + "type": "integer" + }, + "is_export": { + "description": "1-导出", + "type": "integer" + }, + "make_time_end": { + "description": "制单结束时间", + "type": "string" + }, + "make_time_start": { + "description": "制单开始时间", + "type": "string" + }, + "member_level": { + "description": "会员等级:普通用户、尊享会员", + "type": "integer" + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "页面条数", + "type": "integer" + }, + "repair_unit_id": { + "description": "维修单位ID(供应商)", + "type": "integer" + }, + "send_time_end": { + "description": "送修结束时间", + "type": "string" + }, + "send_time_start": { + "description": "送修开始时间", + "type": "string" + }, + "serial_number": { + "description": "单据编号", + "type": "string" + }, + "store_id": { + "description": "门店id", + "type": "array", + "items": { + "type": "integer" + } + }, + "tel": { + "description": "手机号", + "type": "string" + }, + "uid": { + "description": "用户ID", + "type": "integer" + } + } + }, + "models.ErpRepairDetailReportResp": { + "type": "object", + "properties": { + "export_url": { + "description": "导出excel地址", + "type": "string" + }, + "list": { + "description": "零售明细", + "type": "array", + "items": { + "$ref": "#/definitions/models.RepairRecord" + } + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页展示条数", + "type": "integer" + }, + "sumData": { + "description": "汇总数据", + "allOf": [ + { + "$ref": "#/definitions/models.RepairDetailTotalData" + } + ] + }, + "total": { + "description": "总条数(总订单数)", + "type": "integer" + } + } + }, "models.ErpRepairDetailReq": { "type": "object", "required": [ @@ -14026,6 +14408,10 @@ const docTemplate = `{ "description": "页面条数", "type": "integer" }, + "repair_unit_id": { + "description": "维修单位ID(供应商)", + "type": "integer" + }, "send_time_end": { "description": "送修结束时间", "type": "string" @@ -14088,14 +14474,23 @@ const docTemplate = `{ "models.ErpRepairSendReq": { "type": "object", "required": [ + "express_type", "repair_unit_id", "serial_number" ], "properties": { + "express_fee": { + "description": "快递费用", + "type": "number" + }, "express_no": { "description": "快递单号", "type": "string" }, + "express_type": { + "description": "邮寄方:1-个人;2-门店", + "type": "integer" + }, "repair_unit_id": { "description": "维修单位ID(供应商)", "type": "integer" @@ -17557,6 +17952,121 @@ const docTemplate = `{ } } }, + "models.MarketStoreSalesData": { + "type": "object", + "properties": { + "count": { + "description": "销售数量", + "type": "integer" + }, + "promotion_fee": { + "description": "推广费", + "type": "number" + }, + "sales_amount": { + "description": "销售额", + "type": "number" + }, + "sales_profit": { + "description": "销售毛利", + "type": "number" + }, + "staff_profit": { + "description": "员工毛利", + "type": "number" + }, + "store_id": { + "description": "门店id", + "type": "integer" + }, + "store_name": { + "description": "门店名称", + "type": "string" + } + } + }, + "models.MarketStoreSalesDataReq": { + "type": "object", + "properties": { + "end_time": { + "description": "结束时间", + "type": "string" + }, + "is_export": { + "description": "1-导出", + "type": "integer" + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "页面条数", + "type": "integer" + }, + "sort_type": { + "description": "排序类型:desc 降序、asc 升序", + "type": "string" + }, + "start_time": { + "description": "开始时间", + "type": "string" + }, + "store_id": { + "description": "门店ID", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "models.MarketStoreSalesDataResp": { + "type": "object", + "properties": { + "export_url": { + "type": "string" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/models.MarketStoreSalesData" + } + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页展示条数", + "type": "integer" + }, + "total": { + "description": "总条数", + "type": "integer" + }, + "total_count": { + "description": "总销售数量", + "type": "integer" + }, + "total_promotion_fee": { + "description": "总推广费", + "type": "number" + }, + "total_sales_amount": { + "description": "总销售额", + "type": "number" + }, + "total_sales_profit": { + "description": "总销售毛利", + "type": "number" + }, + "total_staff_profit": { + "description": "总员工毛利", + "type": "number" + } + } + }, "models.Menu": { "type": "object", "properties": { @@ -19090,9 +19600,6 @@ const docTemplate = `{ "description": "经手人名称", "type": "string" }, - "member_level": { - "type": "integer" - }, "store_id": { "type": "integer" }, @@ -19104,6 +19611,23 @@ const docTemplate = `{ } } }, + "models.RepairDetailTotalData": { + "type": "object", + "properties": { + "count": { + "description": "维修数量", + "type": "integer" + }, + "total_express_fee": { + "description": "快递费用", + "type": "number" + }, + "total_repair_fee": { + "description": "维修费用", + "type": "number" + } + } + }, "models.RepairEditReq": { "type": "object", "required": [ @@ -19239,10 +19763,18 @@ const docTemplate = `{ "description": "创建时间", "type": "string" }, + "express_fee": { + "description": "快递费用", + "type": "number" + }, "express_no": { "description": "快递单号", "type": "string" }, + "express_type": { + "description": "邮寄方:1-个人;2-门店", + "type": "integer" + }, "handler_id": { "description": "经手人ID", "type": "integer" @@ -20238,6 +20770,10 @@ const docTemplate = `{ "description": "在线", "type": "integer" }, + "landline": { + "description": "固定电话", + "type": "string" + }, "latitude": { "description": "纬度", "type": "number" @@ -20263,7 +20799,7 @@ const docTemplate = `{ "type": "number" }, "tel": { - "description": "电话", + "description": "电话(手机号)", "type": "string" }, "updatedAt": { diff --git a/docs/swagger.json b/docs/swagger.json index 916707d..b249f85 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2020,6 +2020,105 @@ } } }, + "/api/v1/erp_order/business/create": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "商场检查" + ], + "summary": "创建或更新每日经营数据", + "parameters": [ + { + "description": "每日经营数据模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateBusinessSummaryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/app.Response" + } + } + } + } + }, + "/api/v1/erp_order/business/list": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "商场检查" + ], + "summary": "获取每日经营数据列表", + "parameters": [ + { + "description": "获取每日经营数据列表请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.BusinessSummaryListRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.BusinessSummaryListResp" + } + } + } + } + }, + "/api/v1/erp_order/business/summary_data": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "商场检查" + ], + "summary": "查询门店经营汇总数据", + "parameters": [ + { + "description": "查询门店经营数据模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.MarketStoreSalesDataReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.MarketStoreSalesDataResp" + } + } + } + } + }, "/api/v1/erp_order/create": { "post": { "consumes": [ @@ -6074,6 +6173,39 @@ } } }, + "/api/v1/repair/detail_report": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "维修管理,V1.4.6" + ], + "summary": "维修明细", + "parameters": [ + { + "description": "维修明细模型", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ErpRepairDetailReportReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ErpRepairDetailReportResp" + } + } + } + } + }, "/api/v1/repair/edit": { "post": { "consumes": [ @@ -8128,6 +8260,103 @@ } } }, + "models.BusinessSummaryItem": { + "type": "object", + "properties": { + "count": { + "description": "销售数量", + "type": "integer" + }, + "date": { + "description": "时间,如:\"2023-12-25\"", + "type": "string" + }, + "promotion_fee": { + "description": "推广费", + "type": "number" + }, + "sales_amount": { + "description": "销售额", + "type": "number" + }, + "sales_profit": { + "description": "销售毛利", + "type": "number" + }, + "staff_profit": { + "description": "员工毛利", + "type": "number" + } + } + }, + "models.BusinessSummaryListRequest": { + "type": "object", + "properties": { + "end_time": { + "description": "结束时间", + "type": "string" + }, + "page_index": { + "description": "当前页码", + "type": "integer" + }, + "page_size": { + "description": "每页条数", + "type": "integer" + }, + "start_time": { + "description": "开始时间", + "type": "string" + }, + "store_id": { + "description": "可选,复选门店ID", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "models.BusinessSummaryListResp": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/models.BusinessSummaryItem" + } + }, + "pageIndex": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "total_count": { + "description": "总销售数量", + "type": "integer" + }, + "total_promotion_fee": { + "description": "总推广费", + "type": "number" + }, + "total_sales_amount": { + "description": "总销售额", + "type": "number" + }, + "total_sales_profit": { + "description": "总销售毛利", + "type": "number" + }, + "total_staff_profit": { + "description": "总员工毛利", + "type": "number" + } + } + }, "models.CancelPrivilegeMembersReq": { "type": "object", "required": [ @@ -9156,6 +9385,43 @@ } } }, + "models.CreateBusinessSummaryRequest": { + "type": "object", + "required": [ + "date", + "store_id" + ], + "properties": { + "count": { + "description": "销售数量", + "type": "integer" + }, + "date": { + "description": "格式:yyyy-MM-dd", + "type": "string" + }, + "promotion_fee": { + "description": "推广费", + "type": "number" + }, + "sales_amount": { + "description": "销售额", + "type": "number" + }, + "sales_profit": { + "description": "销售毛利", + "type": "number" + }, + "staff_profit": { + "description": "员工毛利", + "type": "number" + }, + "store_id": { + "description": "门店id", + "type": "integer" + } + } + }, "models.CreateErpPurchaseDemandReq": { "type": "object", "properties": { @@ -13960,6 +14226,122 @@ } } }, + "models.ErpRepairDetailReportReq": { + "type": "object", + "properties": { + "audit_time_end": { + "description": "审核结束时间", + "type": "string" + }, + "audit_time_start": { + "description": "审核开始时间", + "type": "string" + }, + "complete_time_end": { + "description": "维修完成结束时间", + "type": "string" + }, + "complete_time_start": { + "description": "维修完成开始时间", + "type": "string" + }, + "handler_id": { + "description": "经手人id", + "type": "integer" + }, + "is_export": { + "description": "1-导出", + "type": "integer" + }, + "make_time_end": { + "description": "制单结束时间", + "type": "string" + }, + "make_time_start": { + "description": "制单开始时间", + "type": "string" + }, + "member_level": { + "description": "会员等级:普通用户、尊享会员", + "type": "integer" + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "页面条数", + "type": "integer" + }, + "repair_unit_id": { + "description": "维修单位ID(供应商)", + "type": "integer" + }, + "send_time_end": { + "description": "送修结束时间", + "type": "string" + }, + "send_time_start": { + "description": "送修开始时间", + "type": "string" + }, + "serial_number": { + "description": "单据编号", + "type": "string" + }, + "store_id": { + "description": "门店id", + "type": "array", + "items": { + "type": "integer" + } + }, + "tel": { + "description": "手机号", + "type": "string" + }, + "uid": { + "description": "用户ID", + "type": "integer" + } + } + }, + "models.ErpRepairDetailReportResp": { + "type": "object", + "properties": { + "export_url": { + "description": "导出excel地址", + "type": "string" + }, + "list": { + "description": "零售明细", + "type": "array", + "items": { + "$ref": "#/definitions/models.RepairRecord" + } + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页展示条数", + "type": "integer" + }, + "sumData": { + "description": "汇总数据", + "allOf": [ + { + "$ref": "#/definitions/models.RepairDetailTotalData" + } + ] + }, + "total": { + "description": "总条数(总订单数)", + "type": "integer" + } + } + }, "models.ErpRepairDetailReq": { "type": "object", "required": [ @@ -14015,6 +14397,10 @@ "description": "页面条数", "type": "integer" }, + "repair_unit_id": { + "description": "维修单位ID(供应商)", + "type": "integer" + }, "send_time_end": { "description": "送修结束时间", "type": "string" @@ -14077,14 +14463,23 @@ "models.ErpRepairSendReq": { "type": "object", "required": [ + "express_type", "repair_unit_id", "serial_number" ], "properties": { + "express_fee": { + "description": "快递费用", + "type": "number" + }, "express_no": { "description": "快递单号", "type": "string" }, + "express_type": { + "description": "邮寄方:1-个人;2-门店", + "type": "integer" + }, "repair_unit_id": { "description": "维修单位ID(供应商)", "type": "integer" @@ -17546,6 +17941,121 @@ } } }, + "models.MarketStoreSalesData": { + "type": "object", + "properties": { + "count": { + "description": "销售数量", + "type": "integer" + }, + "promotion_fee": { + "description": "推广费", + "type": "number" + }, + "sales_amount": { + "description": "销售额", + "type": "number" + }, + "sales_profit": { + "description": "销售毛利", + "type": "number" + }, + "staff_profit": { + "description": "员工毛利", + "type": "number" + }, + "store_id": { + "description": "门店id", + "type": "integer" + }, + "store_name": { + "description": "门店名称", + "type": "string" + } + } + }, + "models.MarketStoreSalesDataReq": { + "type": "object", + "properties": { + "end_time": { + "description": "结束时间", + "type": "string" + }, + "is_export": { + "description": "1-导出", + "type": "integer" + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "页面条数", + "type": "integer" + }, + "sort_type": { + "description": "排序类型:desc 降序、asc 升序", + "type": "string" + }, + "start_time": { + "description": "开始时间", + "type": "string" + }, + "store_id": { + "description": "门店ID", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "models.MarketStoreSalesDataResp": { + "type": "object", + "properties": { + "export_url": { + "type": "string" + }, + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/models.MarketStoreSalesData" + } + }, + "pageIndex": { + "description": "页码", + "type": "integer" + }, + "pageSize": { + "description": "每页展示条数", + "type": "integer" + }, + "total": { + "description": "总条数", + "type": "integer" + }, + "total_count": { + "description": "总销售数量", + "type": "integer" + }, + "total_promotion_fee": { + "description": "总推广费", + "type": "number" + }, + "total_sales_amount": { + "description": "总销售额", + "type": "number" + }, + "total_sales_profit": { + "description": "总销售毛利", + "type": "number" + }, + "total_staff_profit": { + "description": "总员工毛利", + "type": "number" + } + } + }, "models.Menu": { "type": "object", "properties": { @@ -19079,9 +19589,6 @@ "description": "经手人名称", "type": "string" }, - "member_level": { - "type": "integer" - }, "store_id": { "type": "integer" }, @@ -19093,6 +19600,23 @@ } } }, + "models.RepairDetailTotalData": { + "type": "object", + "properties": { + "count": { + "description": "维修数量", + "type": "integer" + }, + "total_express_fee": { + "description": "快递费用", + "type": "number" + }, + "total_repair_fee": { + "description": "维修费用", + "type": "number" + } + } + }, "models.RepairEditReq": { "type": "object", "required": [ @@ -19228,10 +19752,18 @@ "description": "创建时间", "type": "string" }, + "express_fee": { + "description": "快递费用", + "type": "number" + }, "express_no": { "description": "快递单号", "type": "string" }, + "express_type": { + "description": "邮寄方:1-个人;2-门店", + "type": "integer" + }, "handler_id": { "description": "经手人ID", "type": "integer" @@ -20227,6 +20759,10 @@ "description": "在线", "type": "integer" }, + "landline": { + "description": "固定电话", + "type": "string" + }, "latitude": { "description": "纬度", "type": "number" @@ -20252,7 +20788,7 @@ "type": "number" }, "tel": { - "description": "电话", + "description": "电话(手机号)", "type": "string" }, "updatedAt": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 1cfb829..1bba6c8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -367,6 +367,75 @@ definitions: required: - print_list_info type: object + models.BusinessSummaryItem: + properties: + count: + description: 销售数量 + type: integer + date: + description: 时间,如:"2023-12-25" + type: string + promotion_fee: + description: 推广费 + type: number + sales_amount: + description: 销售额 + type: number + sales_profit: + description: 销售毛利 + type: number + staff_profit: + description: 员工毛利 + type: number + type: object + models.BusinessSummaryListRequest: + properties: + end_time: + description: 结束时间 + type: string + page_index: + description: 当前页码 + type: integer + page_size: + description: 每页条数 + type: integer + start_time: + description: 开始时间 + type: string + store_id: + description: 可选,复选门店ID + items: + type: integer + type: array + type: object + models.BusinessSummaryListResp: + properties: + list: + items: + $ref: '#/definitions/models.BusinessSummaryItem' + type: array + pageIndex: + type: integer + pageSize: + type: integer + total: + type: integer + total_count: + description: 总销售数量 + type: integer + total_promotion_fee: + description: 总推广费 + type: number + total_sales_amount: + description: 总销售额 + type: number + total_sales_profit: + description: 总销售毛利 + type: number + total_staff_profit: + description: 总员工毛利 + type: number + type: object models.CancelPrivilegeMembersReq: properties: uid: @@ -1114,6 +1183,33 @@ definitions: - wx_app_mchId - wx_app_mchSecret type: object + models.CreateBusinessSummaryRequest: + properties: + count: + description: 销售数量 + type: integer + date: + description: 格式:yyyy-MM-dd + type: string + promotion_fee: + description: 推广费 + type: number + sales_amount: + description: 销售额 + type: number + sales_profit: + description: 销售毛利 + type: number + staff_profit: + description: 员工毛利 + type: number + store_id: + description: 门店id + type: integer + required: + - date + - store_id + type: object models.CreateErpPurchaseDemandReq: properties: erp_commodity_id: @@ -4616,6 +4712,89 @@ definitions: required: - serial_number type: object + models.ErpRepairDetailReportReq: + properties: + audit_time_end: + description: 审核结束时间 + type: string + audit_time_start: + description: 审核开始时间 + type: string + complete_time_end: + description: 维修完成结束时间 + type: string + complete_time_start: + description: 维修完成开始时间 + type: string + handler_id: + description: 经手人id + type: integer + is_export: + description: 1-导出 + type: integer + make_time_end: + description: 制单结束时间 + type: string + make_time_start: + description: 制单开始时间 + type: string + member_level: + description: 会员等级:普通用户、尊享会员 + type: integer + pageIndex: + description: 页码 + type: integer + pageSize: + description: 页面条数 + type: integer + repair_unit_id: + description: 维修单位ID(供应商) + type: integer + send_time_end: + description: 送修结束时间 + type: string + send_time_start: + description: 送修开始时间 + type: string + serial_number: + description: 单据编号 + type: string + store_id: + description: 门店id + items: + type: integer + type: array + tel: + description: 手机号 + type: string + uid: + description: 用户ID + type: integer + type: object + models.ErpRepairDetailReportResp: + properties: + export_url: + description: 导出excel地址 + type: string + list: + description: 零售明细 + items: + $ref: '#/definitions/models.RepairRecord' + type: array + pageIndex: + description: 页码 + type: integer + pageSize: + description: 每页展示条数 + type: integer + sumData: + allOf: + - $ref: '#/definitions/models.RepairDetailTotalData' + description: 汇总数据 + total: + description: 总条数(总订单数) + type: integer + type: object models.ErpRepairDetailReq: properties: serial_number: @@ -4656,6 +4835,9 @@ definitions: pageSize: description: 页面条数 type: integer + repair_unit_id: + description: 维修单位ID(供应商) + type: integer send_time_end: description: 送修结束时间 type: string @@ -4700,9 +4882,15 @@ definitions: type: object models.ErpRepairSendReq: properties: + express_fee: + description: 快递费用 + type: number express_no: description: 快递单号 type: string + express_type: + description: 邮寄方:1-个人;2-门店 + type: integer repair_unit_id: description: 维修单位ID(供应商) type: integer @@ -4713,6 +4901,7 @@ definitions: description: 单据编号 type: string required: + - express_type - repair_unit_id - serial_number type: object @@ -7198,6 +7387,89 @@ definitions: total: type: integer type: object + models.MarketStoreSalesData: + properties: + count: + description: 销售数量 + type: integer + promotion_fee: + description: 推广费 + type: number + sales_amount: + description: 销售额 + type: number + sales_profit: + description: 销售毛利 + type: number + staff_profit: + description: 员工毛利 + type: number + store_id: + description: 门店id + type: integer + store_name: + description: 门店名称 + type: string + type: object + models.MarketStoreSalesDataReq: + properties: + end_time: + description: 结束时间 + type: string + is_export: + description: 1-导出 + type: integer + pageIndex: + description: 页码 + type: integer + pageSize: + description: 页面条数 + type: integer + sort_type: + description: 排序类型:desc 降序、asc 升序 + type: string + start_time: + description: 开始时间 + type: string + store_id: + description: 门店ID + items: + type: integer + type: array + type: object + models.MarketStoreSalesDataResp: + properties: + export_url: + type: string + list: + items: + $ref: '#/definitions/models.MarketStoreSalesData' + type: array + pageIndex: + description: 页码 + type: integer + pageSize: + description: 每页展示条数 + type: integer + total: + description: 总条数 + type: integer + total_count: + description: 总销售数量 + type: integer + total_promotion_fee: + description: 总推广费 + type: number + total_sales_amount: + description: 总销售额 + type: number + total_sales_profit: + description: 总销售毛利 + type: number + total_staff_profit: + description: 总员工毛利 + type: number + type: object models.Menu: properties: action: @@ -8290,8 +8562,6 @@ definitions: handler_name: description: 经手人名称 type: string - member_level: - type: integer store_id: type: integer tel: @@ -8302,6 +8572,18 @@ definitions: - handler_id - store_id type: object + models.RepairDetailTotalData: + properties: + count: + description: 维修数量 + type: integer + total_express_fee: + description: 快递费用 + type: number + total_repair_fee: + description: 维修费用 + type: number + type: object models.RepairEditReq: properties: audit_time: @@ -8402,9 +8684,15 @@ definitions: createdAt: description: 创建时间 type: string + express_fee: + description: 快递费用 + type: number express_no: description: 快递单号 type: string + express_type: + description: 邮寄方:1-个人;2-门店 + type: integer handler_id: description: 经手人ID type: integer @@ -9132,6 +9420,9 @@ definitions: is_online: description: 在线 type: integer + landline: + description: 固定电话 + type: string latitude: description: 纬度 type: number @@ -9151,7 +9442,7 @@ definitions: description: 门店销售提成比例 type: number tel: - description: 电话 + description: 电话(手机号) type: string updatedAt: description: 更新时间 @@ -11761,6 +12052,69 @@ paths: summary: 审核零售订单 tags: - 零售订单 + /api/v1/erp_order/business/create: + post: + consumes: + - application/json + parameters: + - description: 每日经营数据模型 + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.CreateBusinessSummaryRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/app.Response' + summary: 创建或更新每日经营数据 + tags: + - 商场检查 + /api/v1/erp_order/business/list: + post: + consumes: + - application/json + parameters: + - description: 获取每日经营数据列表请求参数 + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.BusinessSummaryListRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.BusinessSummaryListResp' + summary: 获取每日经营数据列表 + tags: + - 商场检查 + /api/v1/erp_order/business/summary_data: + post: + consumes: + - application/json + parameters: + - description: 查询门店经营数据模型 + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.MarketStoreSalesDataReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.MarketStoreSalesDataResp' + summary: 查询门店经营汇总数据 + tags: + - 商场检查 /api/v1/erp_order/create: post: consumes: @@ -14344,6 +14698,27 @@ paths: summary: 查询维修订单详情 tags: - 维修管理,V1.4.6 + /api/v1/repair/detail_report: + post: + consumes: + - application/json + parameters: + - description: 维修明细模型 + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.ErpRepairDetailReportReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ErpRepairDetailReportResp' + summary: 维修明细 + tags: + - 维修管理,V1.4.6 /api/v1/repair/edit: post: consumes: