Merge remote-tracking branch 'origin/test' into test

This commit is contained in:
yijiegong 2024-09-14 16:24:36 +08:00
commit 50dc84efd5
60 changed files with 2301 additions and 329 deletions

View File

@ -21,6 +21,8 @@ public interface RedisConstant {
String LOCK_KEY = "LOCK:";
String CREATE_ORDER = "CREATE_ORDER";
// 排队取号全局号码
String TABLE_CALL_NUMBER = "TABLE_CALL_NUMBER:";
static String getCurrentOrderKey(String tableId, String shopId) {
return CURRENT_TABLE_ORDER + shopId + ":" + tableId;
@ -37,4 +39,8 @@ public interface RedisConstant {
}
return key.toString();
}
static String getTableCallNumKey(Integer shopId, Integer callTableId) {
return TABLE_CALL_NUMBER + shopId + ":" + callTableId;
}
}

View File

@ -19,4 +19,13 @@ public interface RabbitConstants {
// 售出记录
String EXCHANGE_STOCK_RECORD = "exchange.stock.record";
String ROUTING_STOCK_RECORD_SALE = "routing.stock.record.sale";
// 菜品打印
String EXCHANGE_PRINT = "exchange.print";
String ROUTING_KEY_PRINT_DISHES = "routing.dishes.print";
// 订单打印
String QUEUE_PRINT_PLACE = "queue.place.order.print";
String ROUTING_KEY_PRINT_PLACE = "routing.place.order.print";
}

View File

@ -0,0 +1,117 @@
package cn.ysk.cashier.controller;
import cn.ysk.cashier.annotation.AnonymousAccess;
import cn.ysk.cashier.dto.calltable.*;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.service.app.TbCallService;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
/**
* 叫号
*/
@RestController
@RequestMapping("/callTable")
@AllArgsConstructor
public class TbCallTableController {
private final TbCallService tbCallService;
@AnonymousAccess
@GetMapping
public ResponseEntity<?> get(
@RequestParam(required = false) Integer callTableId,
@RequestParam Integer shopId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) Integer state
) {
return ResponseEntity.ok(tbCallService.get(page, size, shopId, callTableId, state));
}
@AnonymousAccess
@PostMapping
public ResponseEntity<?> add(
@Validated @RequestBody CallTableDTO addCallTableDTO
) {
return ResponseEntity.ok(tbCallService.add(addCallTableDTO));
}
@AnonymousAccess
@PutMapping
public ResponseEntity<?> update(
@Validated @RequestBody UpdateCallTableDTO callTableDTO
) {
return ResponseEntity.ok(tbCallService.update(callTableDTO));
}
@AnonymousAccess
@DeleteMapping
public ResponseEntity<?> delete(
@Validated @RequestBody BaseCallTableDTO baseCallTableDTO
) {
return ResponseEntity.ok(tbCallService.delete(baseCallTableDTO));
}
@AnonymousAccess
@PostMapping("takeNumber")
public ResponseEntity<?> takeNumber(
@Validated @RequestBody TakeNumberDTO takeNumberDTO
) {
return ResponseEntity.ok(tbCallService.takeNumber(takeNumberDTO));
}
@AnonymousAccess
@GetMapping("takeNumberCode")
public ResponseEntity<?> takeNumberCode(
@RequestParam Integer shopId,
@RequestParam Integer callTableId
) {
return ResponseEntity.ok(tbCallService.takeNumberCode(shopId, callTableId));
}
@AnonymousAccess
@PostMapping("call")
public ResponseEntity<?> call(
@Validated @RequestBody CallQueueDTO callQueueDTO
) {
return ResponseEntity.ok(tbCallService.call(callQueueDTO));
}
@AnonymousAccess
@PutMapping("updateState")
public ResponseEntity<?> confirm(
@Validated @RequestBody UpdateCallQueueDTO updateCallQueueDTO
) {
return ResponseEntity.ok(tbCallService.updateInfo(updateCallQueueDTO));
}
@AnonymousAccess
@GetMapping("queue")
public ResponseEntity<?> getQueue(
@RequestParam Integer shopId,
@RequestParam(required = false) Integer callTableId,
@RequestParam(required = false) Integer state,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size
) {
return ResponseEntity.ok(tbCallService.getQueue(shopId, callTableId, state, page, size));
}
@AnonymousAccess
@GetMapping("callRecord")
public ResponseEntity<?> getCallRecord(
@RequestParam Integer shopId,
@RequestParam(required = false) Integer callTableId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size
) {
return ResponseEntity.ok(tbCallService.getCallRecord(shopId, callTableId, page, size));
}
}

View File

@ -86,6 +86,15 @@ public class TbPlaceController {
tbShopTableService.removeCart(removeCartDTO);
return new ResponseEntity<>(HttpStatus.OK);
}
@AnonymousAccess
@PutMapping("/returnCart")
@Log("代客下单 退单")
@ApiOperation("代客下单 清空购物车 /shop/table")
public ResponseEntity<Object> returnOrder(@Validated @RequestBody RemoveCartDTO removeCartDTO) {
tbShopTableService.returnCart(removeCartDTO);
return new ResponseEntity<>(HttpStatus.OK);
}
@AnonymousAccess
@DeleteMapping("/clearCart")
@ -154,7 +163,6 @@ public class TbPlaceController {
public ResponseEntity<Object> pending(
@RequestBody PendingDTO pendingDTO
) {
pendingDTO.setIsPending(true);
return ResponseEntity.ok(tbShopTableService.pending(pendingDTO));
}
@ -192,6 +200,31 @@ public class TbPlaceController {
return ResponseEntity.ok(tbShopTableService.pay(payDTO));
}
@AnonymousAccess
@PutMapping("/choseTable")
@Log("代客下单 选择台桌")
@ApiOperation("代客下单 选择台桌 /shop/table")
public ResponseEntity<Object> choseTable(
@Validated @RequestBody ChoseTableDTO choseTableDTO
) {
return ResponseEntity.ok(tbShopTableService.choseTable(choseTableDTO));
}
@AnonymousAccess
@PutMapping("/choseCount")
@Log("代客下单 选择用餐人数")
@ApiOperation("代客下单 选择台桌 /shop/table")
public ResponseEntity<Object> choseCount(
@Validated @RequestBody ChoseCountDTO choseCountDTO
) {
return ResponseEntity.ok(tbShopTableService.choseCount(choseCountDTO));
}
@AnonymousAccess
@PutMapping("/updateVip")
@Log("代客下单 查询购物车")
@ -202,6 +235,26 @@ public class TbPlaceController {
return ResponseEntity.ok(tbShopTableService.updateVip(updateVipDTO));
}
@AnonymousAccess
@PostMapping("/printOrder")
@Log("代客下单 打印订单")
@ApiOperation("代客下单 查询购物车 /shop/table")
public ResponseEntity<Object> printOrder(
@Validated @RequestBody BaseTableDTO baseTableDTO
) {
return ResponseEntity.ok(tbShopTableService.printOrder(baseTableDTO));
}
@AnonymousAccess
@PostMapping("/printDishes")
@Log("代客下单 打印菜品单")
@ApiOperation("代客下单 打印菜品单 /shop/table")
public ResponseEntity<Object> printDishes(
@Validated @RequestBody BaseTableDTO baseTableDTO
) {
return ResponseEntity.ok(tbShopTableService.printDishes(baseTableDTO));
}
private final RabbitTemplate rabbitTemplate;
@AnonymousAccess
@GetMapping("/test")

View File

@ -2,6 +2,7 @@ package cn.ysk.cashier.controller.shop;
import cn.ysk.cashier.annotation.rest.AnonymousGetMapping;
import cn.ysk.cashier.annotation.rest.AnonymousPostMapping;
import cn.ysk.cashier.dto.BaseQueryDto;
import cn.ysk.cashier.dto.shop.ShopTableSaleInfoDto;
import cn.ysk.cashier.service.SummaryService;
import lombok.RequiredArgsConstructor;
@ -23,11 +24,20 @@ public class SummaryController {
@Resource
private SummaryService summaryService;
@GetMapping
private Object shopSummary(@RequestParam Integer shopId) {
return summaryService.selectSummary(shopId);
@PostMapping("/trade")
@AnonymousPostMapping
private Object shopSummaryDate(@RequestBody BaseQueryDto param) {
return summaryService.trade(param);
}
// @GetMapping
// private Object shopSummary(@RequestParam Integer shopId) {
// return summaryService.selectSummary(shopId);
// }
@GetMapping("/date")
private Object shopSummaryDate(@RequestParam Integer shopId, @RequestParam Integer day) {
return summaryService.selectSummaryDate(shopId, day);

View File

@ -15,10 +15,12 @@
*/
package cn.ysk.cashier.controller.shop;
import cn.ysk.cashier.annotation.AnonymousAccess;
import cn.ysk.cashier.annotation.Log;
import cn.ysk.cashier.dto.shoptable.AddCartDTO;
import cn.ysk.cashier.dto.shoptable.ClearCartDTO;
import cn.ysk.cashier.dto.shoptable.RemoveCartDTO;
import cn.ysk.cashier.dto.shoptable.TableGenerateDTO;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import cn.ysk.cashier.service.shop.TbShopTableService;
import cn.ysk.cashier.dto.shop.TbShopTableQueryCriteria;
@ -87,6 +89,15 @@ public class TbShopTableController {
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping("/generate")
@ApiOperation("查询/shop/table")
public ResponseEntity<Object> generate(@Validated @RequestBody TableGenerateDTO generateDTO){
if (generateDTO.getStart() >= generateDTO.getEnd()){
return ResponseEntity.badRequest().body("起始数不能大于结束数");
}
return new ResponseEntity<>(tbShopTableService.generate(generateDTO),HttpStatus.OK);
}
}

View File

@ -0,0 +1,36 @@
package cn.ysk.cashier.dto;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
public class BaseQueryDto {
private Integer shopId;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
public Integer getShopId() {
return shopId;
}
public void setShopId(Integer shopId) {
this.shopId = shopId;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
}

View File

@ -0,0 +1,13 @@
package cn.ysk.cashier.dto.calltable;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class BaseCallTableDTO {
@NotNull
private Integer callTableId;
@NotNull
private Integer shopId;
}

View File

@ -0,0 +1,13 @@
package cn.ysk.cashier.dto.calltable;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class CallQueueDTO{
@NotNull
private Integer callQueueId;
@NotNull
private Integer shopId;
}

View File

@ -0,0 +1,27 @@
package cn.ysk.cashier.dto.calltable;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class CallTableDTO {
@NotNull
private Integer shopId;
@NotEmpty
private String name;
private String note;
@NotNull
@Min(1)
private Integer waitTime;
@NotBlank
private String prefix;
@NotNull
@Min(1)
private Integer start;
@Min(1)
private Integer nearNum;
}

View File

@ -0,0 +1,15 @@
package cn.ysk.cashier.dto.calltable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
@EqualsAndHashCode(callSuper = true)
@Data
public class TakeNumberDTO extends BaseCallTableDTO{
private Integer userId;
private String phone;
private String note;
private String name;
}

View File

@ -0,0 +1,15 @@
package cn.ysk.cashier.dto.calltable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import javax.validation.constraints.NotNull;
@EqualsAndHashCode(callSuper = true)
@Data
@Getter
public class UpdateCallQueueDTO extends CallQueueDTO{
@NotNull
private Byte state;
}

View File

@ -0,0 +1,26 @@
package cn.ysk.cashier.dto.calltable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class UpdateCallTableDTO{
@NotNull
private Integer callTableId;
@NotNull
private Integer shopId;
private String name;
private String note;
@Min(1)
private Integer waitTime;
private String prefix;
@Min(1)
private Integer start;
@Min(1)
private Integer nearNum;
}

View File

@ -23,6 +23,7 @@ import javax.persistence.Column;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.io.Serializable;
import java.util.List;
/**
* @website https://eladmin.vip
@ -192,4 +193,10 @@ public class TbShopInfoDto implements Serializable {
private String isReturn;
private String isMemberIn;
private String isMemberReturn;
private Integer isTableFee;
private BigDecimal tableFee;
private List<String> eatModel;
//程序码(零点八零首页)
private String smallQrcode;
private String paymentQrcode;
}

View File

@ -36,4 +36,7 @@ public class TbShopTableQueryCriteria{
@Query
private Integer areaId;
}
@Query
private Long qrcode;
}

View File

@ -20,9 +20,10 @@ public class AddCartDTO {
@NotEmpty
private String tableId;
@NotNull
@Min(0)
@Min(1)
private Integer num;
private boolean isPack;
private boolean isGift;
private Integer cartId;
private String note;
}

View File

@ -0,0 +1,15 @@
package cn.ysk.cashier.dto.shoptable;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class BaseTableDTO {
@NotNull
private Integer shopId;
@NotEmpty
private String tableId;
}

View File

@ -0,0 +1,20 @@
package cn.ysk.cashier.dto.shoptable;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class ChoseCountDTO {
@NotEmpty
private String masterId;
@NotNull
private Integer shopId;
@NotEmpty
private String tableId;
@NotNull
@Min(1)
private Integer num;
}

View File

@ -0,0 +1,19 @@
package cn.ysk.cashier.dto.shoptable;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class ChoseTableDTO {
@NotNull
private Integer shopId;
private Integer orderId;
@NotNull
private String newTableId;
@NotEmpty
private String oldTableId;
}

View File

@ -0,0 +1,15 @@
package cn.ysk.cashier.dto.shoptable;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class ReturnOrderDTO {
@NotNull
private Integer cartId;
@NotNull
private Integer shopId;
@NotNull
private Long tableId;
}

View File

@ -0,0 +1,28 @@
package cn.ysk.cashier.dto.shoptable;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class TableGenerateDTO {
@NotNull
private Integer shopId;
@NotEmpty
private String sign;
private Integer areaId;
@Min(1)
@NotNull
private Integer start;
@Min(2)
@Max(1000)
@NotNull
private Integer end;
@NotNull
@Min(1)
private Integer capacity;
private Integer autoClear = 1;
}

View File

@ -2,7 +2,7 @@ package cn.ysk.cashier.enums;
public enum TableStateEnum {
IDLE("idle"),
CLOSED("closed"), PAYING("paying"), PENDING("pending"), USING("using");
CLOSED("closed"), PAYING("paying"), PENDING("pending"), USING("using"), CLEANING("cleaning");
private String state = "closed";
TableStateEnum(String state) {

View File

@ -18,8 +18,13 @@ package cn.ysk.cashier.mapper.shop;
import cn.ysk.cashier.base.BaseMapper;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.dto.shop.TbShopInfoDto;
import cn.ysk.cashier.utils.ListUtil;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* @website https://eladmin.vip
@ -29,4 +34,19 @@ import org.mapstruct.ReportingPolicy;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface TbShopInfoMapper extends BaseMapper<TbShopInfoDto, TbShopInfo> {
// 自定义的字符串到整数列表的转换方法
default List<String> map(String value) {
return ListUtil.stringChangeStringList(value);
}
// 如果需要从DTO转回实体也可能需要实现反向的映射方法
default String map(List<String> values) {
if (!CollectionUtils.isEmpty(values)) {
return "";
}
// 将整数列表转换为由逗号分隔的字符串
return values.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
}
}

View File

@ -0,0 +1,76 @@
package cn.ysk.cashier.mybatis.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.time.Instant;
@Getter
@Setter
@Entity
@Table(name = "tb_call_queue")
public class TbCallQueue {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "call_table_id")
private Integer callTableId;
@Size(max = 255)
@Column(name = "phone")
private String phone;
@Size(max = 255)
@Column(name = "name")
private String name;
@Size(max = 255)
@Column(name = "shop_name")
private String shopName;
@Column(name = "shop_id")
private Integer shopId;
@Column(name = "state")
private Byte state;
@Column(name = "create_time")
private Instant createTime;
@Column(name = "call_time")
private Instant callTime;
@Column(name = "call_count")
private Integer callCount;
@Column(name = "pass_time")
private Instant passTime;
@Column(name = "cancel_time")
private Instant cancelTime;
@Column(name = "confirm_time")
private Instant confirmTime;
@Size(max = 255)
@Column(name = "note")
private String note;
@Column(name = "user_id")
private Integer userId;
@Size(max = 255)
@Column(name = "open_id")
private String openId;
@Column(name = "sub_state")
private Integer subState;
@Column(name = "call_num")
private String callNum;
}

View File

@ -0,0 +1,57 @@
package cn.ysk.cashier.mybatis.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.time.Instant;
@Getter
@Setter
@Entity
@Table(name = "tb_call_table")
public class TbCallTable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
@Size(max = 255)
@Column(name = "name")
private String name;
@Size(max = 255)
@Column(name = "note")
private String note;
@Column(name = "wait_time")
private Integer waitTime;
@Size(max = 255)
@Column(name = "prefix")
private String prefix;
@Column(name = "start")
private Integer start;
@Column(name = "near_num")
private Integer nearNum;
@Column(name = "state")
private Byte state;
@Column(name = "shop_id")
private Integer shopId;
@Size(max = 255)
@Column(name = "qrcode")
private String qrcode;
@Column(name = "create_time")
private Instant createTime;
@Column(name = "update_time")
private Instant updateTime;
}

View File

@ -0,0 +1,8 @@
package cn.ysk.cashier.mybatis.mapper;
import cn.ysk.cashier.pojo.shop.TbShopArea;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface MpShopAreaMapper extends BaseMapper<TbShopArea> {
}

View File

@ -0,0 +1,25 @@
package cn.ysk.cashier.mybatis.mapper;
import cn.ysk.cashier.mybatis.entity.TbCallQueue;
import cn.ysk.cashier.mybatis.vo.CallRecordVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Select;
/**
* @author Administrator
* @description 针对表tb_call_queue的数据库操作Mapper
* @createDate 2024-09-12 15:34:30
* @Entity cn.ysk.cashier.mybatis.entity.TbCallQueue
*/
public interface TbCallQueueMapper extends BaseMapper<TbCallQueue> {
@Select("select a.*, b.note, TIMESTAMPDIFF(SECOND, a.create_time, NOW()) as since_at from tb_call_queue a " +
"left join tb_call_table b on a.call_table_id=b.id " +
"where a.shop_id=#{shopId} and a.call_table_id=#{callTableId}")
Page<CallRecordVO> selectCallRecord(Integer shopId, Integer callTableId, Page<Object> objectPage);
}

View File

@ -0,0 +1,18 @@
package cn.ysk.cashier.mybatis.mapper;
import cn.ysk.cashier.mybatis.entity.TbCallTable;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author Administrator
* @description 针对表tb_call_table的数据库操作Mapper
* @createDate 2024-09-12 15:07:15
* @Entity cn.ysk.cashier.mybatis.entity.TbCallTable
*/
public interface TbCallTableMapper extends BaseMapper<TbCallTable> {
}

View File

@ -5,9 +5,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* @author GYJ
@ -41,6 +41,33 @@ public interface TbShopUserFlowMapper extends BaseMapper<TbShopUserFlow> {
@Param("startTime") String startTime,
@Param("endTime") String endTime);
@Select("<script>" +
"SELECT" +
" COALESCE(SUM(CASE WHEN flow.biz_code IN ('cashMemberIn', 'inMoneyIn', 'scanMemberIn') THEN flow.amount ELSE 0 END),0) AS 'inAmount'," +
" COALESCE(SUM(CASE WHEN flow.biz_code = 'inMoneyOut' THEN flow.amount ELSE 0 END),0) AS 'outAmount'," +
" COALESCE(SUM(CASE WHEN flow.biz_code IN ('accountGroupPay', 'accountPay', 'consumeOut', 'vipCardCash') THEN flow.amount ELSE 0 END),0) AS 'useAmount'," +
" COALESCE(COUNT(CASE WHEN flow.biz_code IN ('accountGroupPay', 'accountPay', 'consumeOut', 'vipCardCash') THEN 1 ELSE NULL END),0) AS 'useNum'" +
"FROM" +
" tb_shop_user_flow flow" +
" INNER JOIN tb_shop_user vip ON flow.shop_user_id = vip.id and vip.shop_id= #{shopId} " +
"WHERE" +
" flow.create_time BETWEEN #{startTime} AND #{endTime} " +
"</script>")
Map<String, Object> tradeIndexFlow(@Param("shopId") Integer shopId,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
@Select("<script>" +
"SELECT COALESCE(count(1),0) from( " +
" SELECT flow.create_time " +
" FROM tb_shop_user_flow flow " +
" INNER JOIN tb_shop_user vip ON flow.shop_user_id = vip.id AND vip.shop_id = #{shopId} " +
"WHERE biz_code IN ('cashMemberIn', 'inMoneyIn', 'scanMemberIn') " +
"GROUP BY " +
" shop_user_id) as flows where flows.create_time BETWEEN #{startTime} AND #{endTime}" +
"</script>")
Integer tradeIndexNewFlow(@Param("shopId") Integer shopId, @Param("startTime") String startTime, @Param("endTime") String endTime);
@Select(value = "select * from tb_shop_user_flow where shop_user_id=#{userId} order by id desc limit #{page},#{size}")
List<TbShopUserFlow> selectByUserId(@Param("userId") Integer userId,@Param("page") Integer page,@Param("size") Integer size);

View File

@ -1,29 +1,22 @@
package cn.ysk.cashier.mybatis.rest;
import cn.hutool.core.io.IoUtil;
import cn.hutool.http.HttpUtil;
import cn.ysk.cashier.annotation.Log;
import cn.ysk.cashier.domain.QiniuContent;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.entity.Activate;
import cn.ysk.cashier.mybatis.service.ShopService;
import cn.ysk.cashier.service.QiNiuService;
import cn.ysk.cashier.service.WxService;
import cn.ysk.cashier.utils.CacheKey;
import cn.ysk.cashier.utils.RedisUtils;
import cn.ysk.cashier.utils.SecurityUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.io.InputStream;
import java.util.Map;
@Slf4j
@ -32,15 +25,9 @@ import java.util.Map;
@Api(tags = "/shop/storage")
@RequestMapping("/shop/storage")
public class StorageController {
@Autowired
private ShopService shopService;
private final ShopService shopService;
private final RedisUtils redisUtils;
private final QiNiuService qiNiuService;
private static final String APP_ID = "wxd88fffa983758a30";
private static final String APP_SECRET = "a34a61adc0602118b49400baa8812454";
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
private static final String QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacode";
private final WxService wxService;
@GetMapping("/findActivate")
public ResponseEntity<Object> findActivate(@RequestParam String shopId){
@ -72,38 +59,10 @@ public class StorageController {
*/
@PostMapping("/getwxacode")
public ResponseEntity<Object> getwxacode(@RequestBody Map<String, Object> params) {
if (CollectionUtils.isEmpty(params) || !params.containsKey("shopId")) throw new BadRequestException("参数错误");
if (redisUtils.hasKey(CacheKey.VIPCODE + params.get("shopId"))) return new ResponseEntity<>(redisUtils.get(CacheKey.VIPCODE + params.get("shopId")),HttpStatus.OK);
try {
InputStream qrCodeStream = fetchQRCode(params);
QiniuContent qiniuContent = qiNiuService.uploadByte(IoUtil.readBytes(qrCodeStream),qiNiuService.findCloud());
redisUtils.set(CacheKey.VIPCODE + params.get("shopId"),qiniuContent.getUrl());
return new ResponseEntity<>(qiniuContent.getUrl(),HttpStatus.OK);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to generate QR code: " + e.getMessage());
}
}
private String getAccessToken() {
String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s", TOKEN_URL, APP_ID, APP_SECRET);
String response = HttpUtil.get(url);
JSONObject jsonResponse = JSONObject.parseObject(response);
if (!jsonResponse.containsKey("access_token")) {
throw new RuntimeException("Failed to retrieve access token: " + response);
}
return jsonResponse.getString("access_token");
}
private InputStream fetchQRCode(Map<String, Object> params) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("path", "pages/member/index?shopId="+params.get("shopId"));//路径
jsonObject.addProperty("is_hyaline", true);//是否需要透明底色 true 生成透明底色的小程序码
if (params.containsKey("env_version")) jsonObject.addProperty("env_version", "trial");//正式版为 release体验版为 trial开发版为 develop
String accessToken = getAccessToken();
String url = String.format("%s?access_token=%s", QR_CODE_URL, accessToken);
return HttpUtil.createPost(url)
.body(jsonObject.toString(), "application/json")
.execute()
.bodyStream();
if (CollectionUtils.isEmpty(params) || !params.containsKey("shopId"))
throw new BadRequestException("参数错误");
if (redisUtils.hasKey(CacheKey.VIPCODE + params.get("shopId")))
return new ResponseEntity<>(redisUtils.get(CacheKey.VIPCODE + params.get("shopId")), HttpStatus.OK);
return new ResponseEntity<>(wxService.getFetchQRCode(params), HttpStatus.OK);
}
}

View File

@ -0,0 +1,7 @@
package cn.ysk.cashier.mybatis.service;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import com.baomidou.mybatisplus.extension.service.IService;
public interface MpShopTableService extends IService<TbShopTable> {
}

View File

@ -0,0 +1,13 @@
package cn.ysk.cashier.mybatis.service;
import cn.ysk.cashier.mybatis.entity.TbCallQueue;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author Administrator
* @description 针对表tb_call_queue的数据库操作Service
* @createDate 2024-09-12 15:34:30
*/
public interface TbCallQueueService extends IService<TbCallQueue> {
}

View File

@ -0,0 +1,13 @@
package cn.ysk.cashier.mybatis.service;
import cn.ysk.cashier.mybatis.entity.TbCallTable;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author Administrator
* @description 针对表tb_call_table的数据库操作Service
* @createDate 2024-09-12 15:07:15
*/
public interface TbCallTableService extends IService<TbCallTable> {
}

View File

@ -0,0 +1,15 @@
package cn.ysk.cashier.mybatis.service.impl;
import cn.ysk.cashier.mapper.shop.TbShopTableMapper;
import cn.ysk.cashier.mybatis.entity.TagProductDepts;
import cn.ysk.cashier.mybatis.mapper.MpShopTableMapper;
import cn.ysk.cashier.mybatis.mapper.TagProductDeptsMapper;
import cn.ysk.cashier.mybatis.service.MpShopTableService;
import cn.ysk.cashier.mybatis.service.TagProductDeptsService;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class MpShopTableServiceImpl extends ServiceImpl<MpShopTableMapper, TbShopTable> implements MpShopTableService {
}

View File

@ -0,0 +1,22 @@
package cn.ysk.cashier.mybatis.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.ysk.cashier.mybatis.entity.TbCallQueue;
import cn.ysk.cashier.mybatis.service.TbCallQueueService;
import cn.ysk.cashier.mybatis.mapper.TbCallQueueMapper;
import org.springframework.stereotype.Service;
/**
* @author Administrator
* @description 针对表tb_call_queue的数据库操作Service实现
* @createDate 2024-09-12 15:34:30
*/
@Service
public class TbCallQueueServiceImpl extends ServiceImpl<TbCallQueueMapper, TbCallQueue>
implements TbCallQueueService{
}

View File

@ -0,0 +1,22 @@
package cn.ysk.cashier.mybatis.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.ysk.cashier.mybatis.entity.TbCallTable;
import cn.ysk.cashier.mybatis.service.TbCallTableService;
import cn.ysk.cashier.mybatis.mapper.TbCallTableMapper;
import org.springframework.stereotype.Service;
/**
* @author Administrator
* @description 针对表tb_call_table的数据库操作Service实现
* @createDate 2024-09-12 15:07:15
*/
@Service
public class TbCallTableServiceImpl extends ServiceImpl<TbCallTableMapper, TbCallTable>
implements TbCallTableService{
}

View File

@ -0,0 +1,15 @@
package cn.ysk.cashier.mybatis.vo;
import cn.ysk.cashier.mybatis.entity.TbCallQueue;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.sql.Date;
import java.time.Instant;
@EqualsAndHashCode(callSuper = true)
@Data
public class CallRecordVO extends TbCallQueue {
private String note;
private Long sinceAt;
}

View File

@ -155,6 +155,10 @@ public class TbCashierCart implements Serializable {
private String uuid;
@Column(name = "`sku_name`")
private String skuName;
@Column(name = "`place_num`")
private Integer placeNum;
@Column(name = "`note`")
private String note;
public void copy(TbCashierCart source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));

View File

@ -15,6 +15,7 @@
*/
package cn.ysk.cashier.pojo.order;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
@ -101,12 +102,23 @@ public class TbOrderDetail implements Serializable {
@Transient
@ApiModelProperty(value = "退单数量")
@TableField(exist = false)
private Integer refundNumber;
@Column(name = "`is_vip`")
@ApiModelProperty(value = "isVip")
private Integer isVip;
@Column(name = "`place_num`")
@ApiModelProperty(value = "place_num")
private Integer placeNum;
@Column(name = "`note`")
private String note;
@Column(name = "`cart_id`")
private Integer cartId;
public void copy(TbOrderDetail source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}

View File

@ -228,6 +228,9 @@ public class TbOrderInfo implements Serializable {
private String isUseCoupon;
@Column(name = "`use_type`")
private String useType;
@Column(name = "`place_num`")
@ApiModelProperty(value = "place_num")
private Integer placeNum;
public void copy(TbOrderInfo source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}

View File

@ -57,8 +57,7 @@ public class TbShopInfo implements Serializable {
@ApiModelProperty(value = "商户Id")
private String merchantId;
@Column(name = "`shop_name`",nullable = false)
@NotBlank
@Column(name = "`shop_name`")
@ApiModelProperty(value = "店铺名称")
private String shopName;
@ -190,8 +189,7 @@ public class TbShopInfo implements Serializable {
@ApiModelProperty(value = "到期时间")
private Long expireAt;
@Column(name = "`status`",nullable = false)
@NotNull
@Column(name = "`status`")
@ApiModelProperty(value = "门店状态")
private Integer status;
@ -258,15 +256,30 @@ public class TbShopInfo implements Serializable {
@ApiModelProperty(value = "是否允许用户自定义金额")
private String isMemberIn;
@Column(name = "is_member_return")
@ApiModelProperty(value = "是否允许用户自定义金额")
private String isMemberReturn;
@Column(name = "is_table_fee")
@ApiModelProperty(value = "是否免除桌位费 0否1是")
private Integer isTableFee;
@Column(name = "table_fee")
@ApiModelProperty(value = "桌位费")
private BigDecimal tableFee;
@Column(name = "eat_model")
@ApiModelProperty(value = "就餐模式 堂食 dine-in 外带 take-out")
private String eatModel;
@Column(name = "small_qrcode")
@ApiModelProperty(value = "程序码(零点八零首页)")
private String smallQrcode;
@Column(name = "payment_qrcode")
@ApiModelProperty(value = "店铺收款码")
private String paymentQrcode;
public void copy(TbShopInfo source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(false));
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

View File

@ -15,6 +15,7 @@
*/
package cn.ysk.cashier.pojo.shop;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
@ -23,6 +24,8 @@ import javax.persistence.*;
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.io.Serializable;
import java.sql.Date;
import java.sql.Timestamp;
/**
* @website https://eladmin.vip
@ -104,6 +107,24 @@ public class TbShopTable implements Serializable {
@ApiModelProperty(value = "二维码")
private String qrcode = "";
@Column(name = "`auto_clear`")
@ApiModelProperty(value = "自动清台")
private Integer autoClear=1;
@Column(name = "`use_time`")
private Date useTime;
@Column(name = "`end_time`")
private Date endTime;
@Column(name = "`product_num`")
private Integer productNum;
@Column(name = "`total_amount`")
private Integer totalAmount;
@Column(name = "`real_amount`")
private Integer realAmount;
@Column(name = "`use_num`")
private Integer useNum;
public void copy(TbShopTable source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}

View File

@ -31,6 +31,7 @@ import javax.persistence.Tuple;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @website https://eladmin.vip
@ -54,6 +55,22 @@ public interface TbOrderInfoRepository extends JpaRepository<TbOrderInfo, Intege
"AND info.createdAt>:start AND info.createdAt<:end")
TbOrderPayCountVo queryTbOrderRefund(@Param("shopId")String shopId, @Param("tableName") String tableName ,@Param("start") Long start, @Param("end") Long end);
@Query(value = "SELECT " +
"COALESCE(SUM( info.order_amount ),0) AS 'saleAmount'," +
"COALESCE(count( 1 ),0) AS 'saleNum'," +
"COALESCE(sum( discount_amount ),0) AS 'saveAmount'," +
"COALESCE(COUNT( CASE WHEN discount_amount IS NOT NULL AND discount_amount > 0.00 THEN 1 ELSE NULL END ),0) AS 'saveNum' " +
"FROM tb_order_info info " +
"WHERE info.shop_id = :shopId " +
"AND info.created_at > :startTime AND info.created_at < :endTime " +
"AND ((info.STATUS = 'closed' ) OR ( info.STATUS = 'refund' AND info.order_type != 'return' ))", nativeQuery = true)
Map<String,Object> tradeIndex(@Param("shopId") Integer shopId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);
@Query(value = "SELECT COALESCE(SUM(info.order_amount),0) FROM tb_order_info info " +
"WHERE info.shop_id = :shopId AND info.order_type = 'return' " +
"AND info.created_at > :startTime AND info.created_at < :endTime", nativeQuery = true)
BigDecimal tradeIndexRefund(@Param("shopId") Integer shopId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);
@Query(value = "SELECT COUNT(1) ,pay_type AS payType FROM tb_order_info Where shop_id = :shopId AND " +
" created_at BETWEEN :startTime AND :endTime AND status='closed'AND order_type <>'return' GROUP BY pay_type", nativeQuery = true)
List<Object[]> countByShopId(@Param("shopId") String shopId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);

View File

@ -1,5 +1,6 @@
package cn.ysk.cashier.service;
import cn.ysk.cashier.dto.BaseQueryDto;
import cn.ysk.cashier.dto.ShopSummaryDto;
import cn.ysk.cashier.dto.shop.ShopTableSaleInfoDto;
import cn.ysk.cashier.vo.ShopTableSaleInfoVo;
@ -17,8 +18,9 @@ import java.util.Map;
* @author lyf
*/
public interface SummaryService {
SummaryVO selectSummary(Integer shop);
Map<String,Object> trade(BaseQueryDto param);
// SummaryVO selectSummary(Integer shop);
Map<String,Object> selectSummaryDate(Integer shopId, Integer day);
Map<String,Object> selectSummaryToday(Integer shopId);

View File

@ -7,14 +7,15 @@ import cn.ysk.cashier.pojo.order.TbOrderInfo;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
public interface TbPayService {
TbOrderInfo scanPay(PayDTO payDTO);
TbOrderInfo scanPay(Integer shopId, String code, Integer merchantId, Integer memberId, BigDecimal payMount, TbOrderInfo orderInfo);
TbOrderInfo vipPay(@NotNull Integer shopId, @NotNull Integer orderId, Double discount, Integer vipUserId);
void vipPay(BigDecimal payMount, Integer vipUserId);
TbOrderInfo cashPay(PayDTO payDTO);
TbOrderInfo memberAccountPay(String memberId, String shopId, String accountCode, Integer orderId, Double discount) throws Exception;
TbOrderInfo memberAccountPay(String memberId, String shopId, String accountCode, TbOrderInfo orderInfo, BigDecimal payMount) throws Exception;
}

View File

@ -0,0 +1,10 @@
package cn.ysk.cashier.service;
import java.util.Map;
public interface WxService {
String getFetchQRCode(Map<String, Object> params);
String getSmallQrcode(String shopId);
}

View File

@ -0,0 +1,25 @@
package cn.ysk.cashier.service.app;
import cn.ysk.cashier.dto.calltable.*;
public interface TbCallService {
Object add(CallTableDTO addCallTableDTO);
Object update(UpdateCallTableDTO callTableDTO);
Object delete(BaseCallTableDTO baseCallTableDTO);
Object takeNumber(TakeNumberDTO takeNumber);
Object call(CallQueueDTO callQueueDTO);
Object updateInfo(UpdateCallQueueDTO updateCallQueueDTO);
Object get(Integer page, Integer size, Integer shopId, Integer callTableId, Integer state);
Object takeNumberCode(Integer shopId, Integer callTableId);
Object getQueue(Integer shopId, Integer callTableId, Integer state, Integer page, Integer size);
Object getCallRecord(Integer shopId, Integer callTableId, Integer page, Integer size);
}

View File

@ -1,15 +1,19 @@
package cn.ysk.cashier.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.ysk.cashier.dto.ShopSummaryDto;
import cn.ysk.cashier.dto.BaseQueryDto;
import cn.ysk.cashier.dto.shop.ShopTableSaleInfoDto;
import cn.ysk.cashier.enums.PayTypeEnum;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.mapper.TbShopUserFlowMapper;
import cn.ysk.cashier.mybatis.service.TbShopUserFlowService;
import cn.ysk.cashier.repository.ShopUserDutyDetailRepository;
import cn.ysk.cashier.repository.ShopUserDutyRepository;
import cn.ysk.cashier.repository.TbTokenRepository;
import cn.ysk.cashier.repository.order.TbOrderDetailRepository;
import cn.ysk.cashier.repository.order.TbOrderInfoRepository;
import cn.ysk.cashier.repository.shop.TbShopTableRepository;
import cn.ysk.cashier.repository.shop.TbShopUserRepository;
import cn.ysk.cashier.service.SummaryService;
import cn.ysk.cashier.utils.DateUtil;
@ -56,106 +60,167 @@ public class SummaryServiceImpl implements SummaryService {
@Resource
private TbTokenRepository tbTokenRepository;
@Autowired
private TbShopUserFlowMapper tbShopUserFlowMapper;
@Resource
private TbShopUserFlowService tbShopUserFlowService;
@Autowired
private TbOrderDetailRepository tbOrderDetailRepository;
private final TbShopTableRepository tbShopTableRepository;
@Override
public SummaryVO selectSummary(Integer shopId) {
SummaryVO summaryVO = new SummaryVO();
//支付笔数,
Tuple result = tbOrderInfoRepository.countByShopId(shopId.toString());
summaryVO.setPaymentsNumber(result.get(0, Long.class) == null ? 0L : result.get(0, Long.class));
summaryVO.setTotalSales(result.get(1, BigDecimal.class) == null ? new BigDecimal("0") : result.get(1, BigDecimal.class));
if (summaryVO.getPaymentsNumber() == 0) {
summaryVO.setAverageSales(new BigDecimal("0"));
} else {
summaryVO.setAverageSales(summaryVO.getTotalSales().divide(new BigDecimal(summaryVO.getPaymentsNumber()), 2, RoundingMode.DOWN));
public Map<String, Object> trade(BaseQueryDto param) {
if (ObjectUtil.isNull(param.getShopId())) {
throw new BadRequestException("参数不合法");
}
//用户数
Tuple count = tbShopUserRepository.searchByCount(shopId.toString());
summaryVO.setTotalUser(count == null ? 0L : count.get(0, Long.class));
//支付笔数柱形图
List<Object[]> objects = tbOrderInfoRepository.sumByDateOrderNum(shopId.toString(), DateUtil.getDate30DaysAgo(), DateUtil.getDayEnd());
List<CountDateVO> countDateList = new ArrayList<>();
for (Object[] o : objects) {
CountDateVO countDateVO = new CountDateVO();
BigInteger integers = (BigInteger) o[0];
countDateVO.setCount(new BigDecimal(integers.toString()));
countDateVO.setTradeDay((String) o[1]);
countDateList.add(countDateVO);
Map<String, Object> result = new HashMap<>();
Map<String, Object> sale = new HashMap<>();
Map<String, Object> vip = new HashMap<>();
Map<String, Object> count = new HashMap<>();
//订单 销售金额 saleAmount /订单数 saleNum /优惠金额 saveAmount /优惠笔数 saveNum
Map<String, Object> orderMap = tbOrderInfoRepository.tradeIndex(param.getShopId(), param.getStartTime().getTime(), param.getEndTime().getTime());
//退款金额
BigDecimal refundAmount = tbOrderInfoRepository.tradeIndexRefund(param.getShopId(), param.getStartTime().getTime(), param.getEndTime().getTime());
//会员 充值金额 inAmount /退款金额 outAmount /会员消费金额 useAmount /会员消费笔数 useNum
Map<String, Object> flowMap = tbShopUserFlowMapper.tradeIndexFlow(param.getShopId(), DateUtil.getStrTime(param.getStartTime()), DateUtil.getStrTime(param.getEndTime()));
// 新增会员数
Integer newFlow = tbShopUserFlowMapper.tradeIndexNewFlow(param.getShopId(), DateUtil.getStrTime(param.getStartTime()), DateUtil.getStrTime(param.getEndTime()));
//台桌数
int tables = tbShopTableRepository.countAllByShopId(param.getShopId());
//收款金额
BigDecimal incomeAmount = new BigDecimal(orderMap.get("saleAmount").toString()).subtract(new BigDecimal(orderMap.get("saveAmount").toString()));//订单金额 - 优惠金额
sale.put("totalSaleAmount", incomeAmount.add(refundAmount));//总金额 销售收款金额销售退款金额
sale.put("incomeAmount", incomeAmount);//销售 收款金额
sale.put("refundAmount", refundAmount);//销售 退款金额
sale.put("totalVipAmount", new BigDecimal(flowMap.get("inAmount").toString()).add(new BigDecimal(flowMap.get("outAmount").toString())));//会员总金额
sale.put("inAmount", flowMap.get("inAmount")); //会员 充值金额
sale.put("outAmount", flowMap.get("outAmount"));//会员 退款金额
sale.put("incomeAmountAll",incomeAmount.add(new BigDecimal(flowMap.get("inAmount").toString())));//总实收 销售实收+会员实收
result.put("sale",sale);
vip.put("useAmount", flowMap.get("useAmount"));//会员消费金额
vip.put("newFlow",newFlow);//新增会员数
vip.put("useNum",flowMap.get("useNum"));//会员消费笔数
result.put("vip",vip);
BigDecimal saleAmount = new BigDecimal(orderMap.get("saleAmount").toString()).add(refundAmount);
//客单价
if(saleAmount.compareTo(BigDecimal.ZERO) == 0){
count.put("unitPrice",BigDecimal.ZERO);
}else {
count.put("unitPrice",saleAmount.divide(new BigDecimal(orderMap.get("saleNum").toString()),2,RoundingMode.DOWN));
}
//填充日期
Map<String, CountDateVO> dataMap = new HashMap<>();
for (CountDateVO entry : countDateList) {
String tradeDay = entry.getTradeDay();
BigDecimal countOrder = entry.getCount();
dataMap.put(tradeDay, new CountDateVO(tradeDay, countOrder));
BigDecimal saleNum = new BigDecimal(orderMap.get("saleNum").toString());
if(saleNum.compareTo(BigDecimal.ZERO) == 0 || saleNum.compareTo(new BigDecimal(tables)) < 0){
count.put("turnoverRate","0%");//翻台率
}else {
count.put("turnoverRate",saleNum.divide(new BigDecimal(tables),0,RoundingMode.DOWN).subtract(BigDecimal.ONE).multiply(new BigDecimal("100"))+"%");//翻台率
}
// 获取今天的日期
LocalDate today = LocalDate.now();
// 定义日期格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// 转换为字符串
List<CountDateVO> countDateLists = new ArrayList<>();
for (int i = 0; i < 30; i++) {
LocalDate tradeDayLocalDate = today.minusDays(i);
String tradeDayString = tradeDayLocalDate.format(formatter);
CountDateVO countDateVO;
// 检查数据Map中是否存在该日期的数据
if (dataMap.containsKey(tradeDayString)) {
countDateVO = dataMap.get(tradeDayString);
} else {
// 如果不存在则创建新的SumDateVO对象amount设为0
countDateVO = new CountDateVO(tradeDayString, BigDecimal.ZERO);
}
// 将SumDateVO对象添加到列表中
countDateLists.add(countDateVO);
}
countDateLists.sort((a, b) -> a.getTradeDay().compareTo(b.getTradeDay()));
summaryVO.setCountDateList(countDateLists);
//访问量
Tuple tuple = tbTokenRepository.countByAccountId(shopId);
summaryVO.setTotalVisits(tuple == null ? 0L : tuple.get(0, Long.class));
//访问量柱状图
List<Object[]> objectsVisits = tbTokenRepository.countByMonth(shopId, DateUtil.getDate30DaysAgo(), DateUtil.getDayEnd());
List<CountVisitsVO> visitsList = new ArrayList<>();
for (Object[] o : objectsVisits) {
CountVisitsVO countDateVO = new CountVisitsVO();
countDateVO.setCount((BigInteger) o[0]);
countDateVO.setTradeDay((String) o[1]);
visitsList.add(countDateVO);
}
//填充日期
Map<String, CountVisitsVO> dataVisitsMap = new HashMap<>();
for (CountVisitsVO entry : visitsList) {
String tradeDay = entry.getTradeDay();
BigInteger countOrder = entry.getCount();
dataVisitsMap.put(tradeDay, new CountVisitsVO(tradeDay, countOrder));
}
// 转换为字符串
List<CountVisitsVO> countVisitsLists = new ArrayList<>();
for (int i = 0; i < 30; i++) {
LocalDate tradeDayLocalDate = today.minusDays(i);
String tradeDayString = tradeDayLocalDate.format(formatter);
CountVisitsVO countDateVO;
// 检查数据Map中是否存在该日期的数据
if (dataVisitsMap.containsKey(tradeDayString)) {
countDateVO = dataVisitsMap.get(tradeDayString);
} else {
// 如果不存在则创建新的SumDateVO对象amount设为0
countDateVO = new CountVisitsVO(tradeDayString, BigInteger.ZERO);
}
// 将SumDateVO对象添加到列表中
countVisitsLists.add(countDateVO);
}
countVisitsLists.sort((a, b) -> a.getTradeDay().compareTo(b.getTradeDay()));
summaryVO.setVisitsCountList(countVisitsLists);
return summaryVO;
count.put("saveAmount",orderMap.get("saveAmount"));//优惠金额
count.put("saveNum",orderMap.get("saveNum"));//优惠单数
result.put("count",count);
return result;
}
// @Override
// public SummaryVO selectSummary(Integer shopId) {
// SummaryVO summaryVO = new SummaryVO();
// //支付笔数,
// Tuple result = tbOrderInfoRepository.countByShopId(shopId.toString());
// summaryVO.setPaymentsNumber(result.get(0, Long.class) == null ? 0L : result.get(0, Long.class));
// summaryVO.setTotalSales(result.get(1, BigDecimal.class) == null ? new BigDecimal("0") : result.get(1, BigDecimal.class));
// if (summaryVO.getPaymentsNumber() == 0) {
// summaryVO.setAverageSales(new BigDecimal("0"));
// } else {
// summaryVO.setAverageSales(summaryVO.getTotalSales().divide(new BigDecimal(summaryVO.getPaymentsNumber()), 2, RoundingMode.DOWN));
// }
// //用户数
// Tuple count = tbShopUserRepository.searchByCount(shopId.toString());
// summaryVO.setTotalUser(count == null ? 0L : count.get(0, Long.class));
// //支付笔数柱形图
// List<Object[]> objects = tbOrderInfoRepository.sumByDateOrderNum(shopId.toString(), DateUtil.getDate30DaysAgo(), DateUtil.getDayEnd());
// List<CountDateVO> countDateList = new ArrayList<>();
// for (Object[] o : objects) {
// CountDateVO countDateVO = new CountDateVO();
// BigInteger integers = (BigInteger) o[0];
// countDateVO.setCount(new BigDecimal(integers.toString()));
// countDateVO.setTradeDay((String) o[1]);
// countDateList.add(countDateVO);
// }
// //填充日期
// Map<String, CountDateVO> dataMap = new HashMap<>();
// for (CountDateVO entry : countDateList) {
// String tradeDay = entry.getTradeDay();
// BigDecimal countOrder = entry.getCount();
// dataMap.put(tradeDay, new CountDateVO(tradeDay, countOrder));
// }
// // 获取今天的日期
// LocalDate today = LocalDate.now();
// // 定义日期格式
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// // 转换为字符串
// List<CountDateVO> countDateLists = new ArrayList<>();
// for (int i = 0; i < 30; i++) {
// LocalDate tradeDayLocalDate = today.minusDays(i);
// String tradeDayString = tradeDayLocalDate.format(formatter);
// CountDateVO countDateVO;
// // 检查数据Map中是否存在该日期的数据
// if (dataMap.containsKey(tradeDayString)) {
// countDateVO = dataMap.get(tradeDayString);
// } else {
// // 如果不存在则创建新的SumDateVO对象amount设为0
// countDateVO = new CountDateVO(tradeDayString, BigDecimal.ZERO);
// }
// // 将SumDateVO对象添加到列表中
// countDateLists.add(countDateVO);
// }
// countDateLists.sort((a, b) -> a.getTradeDay().compareTo(b.getTradeDay()));
// summaryVO.setCountDateList(countDateLists);
// //访问量
// Tuple tuple = tbTokenRepository.countByAccountId(shopId);
// summaryVO.setTotalVisits(tuple == null ? 0L : tuple.get(0, Long.class));
// //访问量柱状图
// List<Object[]> objectsVisits = tbTokenRepository.countByMonth(shopId, DateUtil.getDate30DaysAgo(), DateUtil.getDayEnd());
// List<CountVisitsVO> visitsList = new ArrayList<>();
// for (Object[] o : objectsVisits) {
// CountVisitsVO countDateVO = new CountVisitsVO();
// countDateVO.setCount((BigInteger) o[0]);
// countDateVO.setTradeDay((String) o[1]);
// visitsList.add(countDateVO);
// }
// //填充日期
// Map<String, CountVisitsVO> dataVisitsMap = new HashMap<>();
// for (CountVisitsVO entry : visitsList) {
// String tradeDay = entry.getTradeDay();
// BigInteger countOrder = entry.getCount();
// dataVisitsMap.put(tradeDay, new CountVisitsVO(tradeDay, countOrder));
// }
// // 转换为字符串
// List<CountVisitsVO> countVisitsLists = new ArrayList<>();
// for (int i = 0; i < 30; i++) {
// LocalDate tradeDayLocalDate = today.minusDays(i);
// String tradeDayString = tradeDayLocalDate.format(formatter);
// CountVisitsVO countDateVO;
// // 检查数据Map中是否存在该日期的数据
// if (dataVisitsMap.containsKey(tradeDayString)) {
// countDateVO = dataVisitsMap.get(tradeDayString);
// } else {
// // 如果不存在则创建新的SumDateVO对象amount设为0
// countDateVO = new CountVisitsVO(tradeDayString, BigInteger.ZERO);
// }
// // 将SumDateVO对象添加到列表中
// countVisitsLists.add(countDateVO);
// }
// countVisitsLists.sort((a, b) -> a.getTradeDay().compareTo(b.getTradeDay()));
// summaryVO.setVisitsCountList(countVisitsLists);
// return summaryVO;
// }
@Override
public Map<String, Object> selectSummaryDate(Integer shopId, Integer day) {
Date startTime = new Date();

View File

@ -106,29 +106,20 @@ public class TbPayServiceImpl implements TbPayService {
}
@Override
public TbOrderInfo scanPay(PayDTO scanPayDTO) {
if (StrUtil.isBlank(scanPayDTO.getCode())) {
public TbOrderInfo scanPay(Integer shopId, String code, Integer merchantId, Integer memberId, BigDecimal payMount, TbOrderInfo orderInfo) {
if (StrUtil.isBlank(code)) {
throw new BadRequestException("无效码");
}
TbOrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaUpdateWrapper<TbOrderInfo>()
.in(TbOrderInfo::getStatus, "unpaid", "paying")
.eq(TbOrderInfo::getId, scanPayDTO.getOrderId())
.eq(TbOrderInfo::getShopId, scanPayDTO.getShopId()));
if (orderInfo == null) {
throw new BadRequestException("订单不存在或已支付");
}
if (ObjectUtil.isNull(orderInfo.getMerchantId()) || ObjectUtil.isEmpty(orderInfo.getMerchantId())) {
if (merchantId == null) {
throw new BadRequestException("订单商户id为空");
}
List<TbCashierCart> cashierCarts = cashierCartMapper.selectList(new LambdaUpdateWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, scanPayDTO.getShopId())
.eq(TbCashierCart::getOrderId, scanPayDTO.getOrderId()));
.eq(TbCashierCart::getShopId, shopId)
.eq(TbCashierCart::getOrderId, orderInfo.getId()));
if (cashierCarts.isEmpty()) {
throw new BadRequestException("购物车为空");
}
@ -138,10 +129,8 @@ public class TbPayServiceImpl implements TbPayService {
body.append(cashierCart.getName());
}
BigDecimal payMount = orderInfo.getOrderAmount().multiply(BigDecimal.valueOf(scanPayDTO.getDiscount())).setScale(2, RoundingMode.HALF_UP);
TbMerchantThirdApply thirdApply = merchantThirdApplyRepository.getById(Integer.valueOf(orderInfo.getMerchantId()));
TbMerchantThirdApply thirdApply = merchantThirdApplyRepository.getById(merchantId);
if (ObjectUtil.isEmpty(thirdApply) || ObjectUtil.isNull(thirdApply)) {
throw new BadRequestException("三方支付信息不存在");
}
@ -149,7 +138,7 @@ public class TbPayServiceImpl implements TbPayService {
String payType;
String payName;
String qpay;
String payTypeCode = scanPayDTO.getCode().substring(0, 2);// 判断收款码
String payTypeCode = code.substring(0, 2);// 判断收款码
if(Integer.parseInt(payTypeCode) >=25 && Integer.parseInt(payTypeCode) <= 30){
payType = "aliPay";
@ -168,32 +157,32 @@ public class TbPayServiceImpl implements TbPayService {
}
long count = shopPayTypeMapper.selectCount(new LambdaUpdateWrapper<TbShopPayType>()
.eq(TbShopPayType::getShopId, scanPayDTO.getShopId())
.eq(TbShopPayType::getShopId, shopId)
.eq(TbShopPayType::getIsDisplay, 1)
.eq(TbShopPayType::getPayType, qpay));
if (count < 1) {
throw new BadRequestException("未到找支付方式");
}
TbOrderPayment payment = orderPaymentService.getById(scanPayDTO.getOrderId());
TbOrderPayment payment = orderPaymentService.getById(orderInfo.getId());
if (ObjectUtil.isEmpty(payment) || payment == null) {
payment = new TbOrderPayment();
payment.setPayTypeId("ysk");
payment.setAmount(payMount.doubleValue());
payment.setPaidAmount(orderInfo.getPayAmount().doubleValue());
payment.setPaidAmount(payMount.doubleValue());
payment.setHasRefundAmount((double) 0);
payment.setPayName(payName);
payment.setPayType(payType);
payment.setReceived(payment.getAmount());
payment.setChangeFee((double) 0);
payment.setMemberId(orderInfo.getMemberId());
payment.setShopId(orderInfo.getShopId());
payment.setOrderId(orderInfo.getId().toString());
payment.setMemberId(String.valueOf(memberId));
payment.setShopId(String.valueOf(shopId));
payment.setOrderId(String.valueOf(orderInfo.getId()));
payment.setCreatedAt(System.currentTimeMillis());
payment.setAuthCode(scanPayDTO.getCode());
payment.setAuthCode(code);
orderPaymentService.save(payment);
} else {
payment.setAuthCode(scanPayDTO.getCode());
payment.setAuthCode(code);
payment.setUpdatedAt(System.currentTimeMillis());
orderPaymentService.updateById(payment);
}
@ -202,15 +191,13 @@ public class TbPayServiceImpl implements TbPayService {
orderInfo.setPayAmount(payMount);
orderInfo.setPayType(qpay);
orderInfo.setUpdatedAt(System.currentTimeMillis());
orderInfoMapper.update(orderInfo, new LambdaUpdateWrapper<TbOrderInfo>()
.eq(TbOrderInfo::getId, scanPayDTO.getOrderId()));
if ("ysk".equals(thirdPayType)) {
ScanPayReq scanPayReq = new ScanPayReq();
scanPayReq.setAppId(thirdApply.getAppId());
scanPayReq.setTimestamp(System.currentTimeMillis());
scanPayReq.setAuthCode(scanPayDTO.getCode());
scanPayReq.setAuthCode(code);
scanPayReq.setNotifyUrl(backUrl);
scanPayReq.setConsumeFee(BigDecimal.valueOf(payment.getAmount()).setScale(2, RoundingMode.DOWN).toPlainString());
@ -231,20 +218,20 @@ public class TbPayServiceImpl implements TbPayService {
orderInfo.setStatus("closed");
orderInfo.setPayOrderNo(object.getJSONObject("data").get("orderNumber").toString());
orderInfoMapper.update(orderInfo, new LambdaQueryWrapper<TbOrderInfo>()
.eq(TbOrderInfo::getId, scanPayDTO.getOrderId()));
.eq(TbOrderInfo::getId, orderInfo.getId()));
//更新购物车状态
TbCashierCart cashierCart = new TbCashierCart();
cashierCart.setStatus("final");
int cartCount = cashierCartMapper.update(cashierCart, new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getOrderId, scanPayDTO.getOrderId()));
.eq(TbCashierCart::getOrderId, orderInfo.getId()));
log.info("更新购物车:{}", cartCount);
//更新子单状态
TbOrderDetail orderDetail = new TbOrderDetail();
orderDetail.setStatus("closed");
orderDetailMapper.update(orderDetail, new LambdaQueryWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getOrderId, scanPayDTO.getOrderId()));
.eq(TbOrderDetail::getOrderId, orderInfo.getId()));
return orderInfo;
@ -282,7 +269,7 @@ public class TbPayServiceImpl implements TbPayService {
reqbody, reqbody,
BigDecimal.valueOf(payment.getAmount()).setScale(2, RoundingMode.DOWN).multiply(new BigDecimal(100)).longValue(),
payType.equals("wechatPay") ? thirdApply.getSmallAppid() : null,
scanPayDTO.getCode(), DateUtils.getSsdfTimes(), thirdApply.getStoreId(), callBack, thirdApply.getAppToken());
code, DateUtils.getSsdfTimes(), thirdApply.getStoreId(), callBack, thirdApply.getAppToken());
log.info("响应信息, {}", publicResp);
if (ObjectUtil.isNotNull(publicResp) && ObjectUtil.isNotEmpty(publicResp)) {
if ("000000".equals(publicResp.getCode())) {
@ -331,41 +318,25 @@ public class TbPayServiceImpl implements TbPayService {
}
@Override
public TbOrderInfo vipPay(@NotNull Integer shopId, @NotNull Integer orderId, Double discount, Integer vipUserId) {
TbOrderInfo orderInfo = orderInfoMapper.selectById(orderId);
if (ObjectUtil.isEmpty(orderInfo)) {
throw new BadRequestException("订单不存在");
}
if (!"unpaid".equals(orderInfo.getStatus()) && !"pending".equals(orderInfo.getStatus())) {
throw new BadRequestException("订单非未支付状态");
}
if (vipUserId != null) {
orderInfo.setUserId(String.valueOf(vipUserId));
}
public void vipPay(BigDecimal payMount, Integer vipUserId) {
// 扣减会员余额
TbShopUser shopUser = shopUserMapper.selectOne(new LambdaUpdateWrapper<TbShopUser>()
.eq(TbShopUser::getStatus, 1)
.eq(TbShopUser::getId, orderInfo.getUserId()));
.eq(TbShopUser::getId, vipUserId));
if (shopUser == null) {
throw new BadRequestException("用户不存在或已被禁用");
}
BigDecimal finalAmount = orderInfo.getOrderAmount().multiply(BigDecimal.valueOf(discount)).setScale(2, RoundingMode.HALF_UP);
long flag = shopUserMapper.decrBalance(Integer.valueOf(orderInfo.getUserId()), finalAmount);
long flag = shopUserMapper.decrBalance(vipUserId, payMount);
if (flag < 1) {
throw new BadRequestException("余额不足或扣除余额失败");
}
TbShopUserFlow userFlow = new TbShopUserFlow();
userFlow.setAmount(finalAmount);
userFlow.setBalance(shopUser.getAmount().subtract(finalAmount));
userFlow.setAmount(payMount);
userFlow.setBalance(shopUser.getAmount().subtract(payMount));
userFlow.setShopUserId(shopUser.getId());
userFlow.setBizCode("vipCardCash");
userFlow.setBizName("余额支付");
@ -373,22 +344,6 @@ public class TbPayServiceImpl implements TbPayService {
userFlow.setType("-");
shopUserFlowMapper.insert(userFlow);
orderInfo.setPayAmount(finalAmount);
orderInfo.setPayType("cash");
orderInfo.setStatus("closed");
orderInfo.setPayOrderNo("cash".concat(SnowFlakeUtil.generateOrderNo()));
orderInfo.setDiscountRatio(BigDecimal.valueOf(discount));
orderInfo.setDiscountAmount(orderInfo.getAmount().subtract(finalAmount));
orderInfoMapper.updateById(orderInfo);
//更新购物车状态
int cartCount = cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
.eq(TbCashierCart::getOrderId, orderId)
.set(TbCashierCart::getStatus, "final"));
orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getOrderId, orderId)
.set(TbOrderDetail::getStatus, "closed"));
return orderInfo;
}
@ -442,33 +397,11 @@ public class TbPayServiceImpl implements TbPayService {
return orderInfo;
}
public TbOrderInfo memberAccountPay(String memberId, String shopId, String accountCode, Integer orderId, Double discount) {
public TbOrderInfo memberAccountPay(String memberId, String shopId, String accountCode, TbOrderInfo orderInfo, BigDecimal payMount) {
if (StrUtil.isBlank(accountCode)) {
throw new BadRequestException("无效码");
}
TbOrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaUpdateWrapper<TbOrderInfo>()
.eq(TbOrderInfo::getId, orderId)
.eq(TbOrderInfo::getShopId, shopId));
if (orderInfo == null) {
throw new BadRequestException("订单不存在或已支付");
}
if (!"unpaid".equals(orderInfo.getStatus()) && !"pending".equals(orderInfo.getStatus())) {
throw new BadRequestException("此订单不处于未支付状态");
}
BigDecimal payMount = orderInfo.getOrderAmount().multiply(BigDecimal.valueOf(discount)).setScale(2, RoundingMode.HALF_UP);
long count = tbShopPayTypeMapper.selectCount(new LambdaQueryWrapper<TbShopPayType>()
.eq(TbShopPayType::getShopId, shopId)
.eq(TbShopPayType::getIsDisplay, 1)
.eq(TbShopPayType::getPayType, "deposit"));
if (count < 1) {
throw new BadRequestException("未知支付方式");
}
TbShopUser user = null;
@ -520,22 +453,7 @@ public class TbPayServiceImpl implements TbPayService {
orderInfo.setUserId(user.getUserId());
orderInfo.setMemberId(user.getId().toString());
orderInfo.setPayType("deposit");
orderInfo.setStatus("closed");
orderInfo.setPayOrderNo("deposit".concat(SnowFlakeUtil.generateOrderNo()));
orderInfoMapper.updateById(orderInfo);
//更新购物车状态
int cartCount = cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
.eq(TbCashierCart::getId, orderId)
.set(TbCashierCart::getStatus, "final"));
orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getOrderId, orderId)
.set(TbOrderDetail::getStatus, "closed"));
log.info("更新购物车:{}", cartCount);
return orderInfo;
}
}

View File

@ -0,0 +1,80 @@
package cn.ysk.cashier.service.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.http.HttpUtil;
import cn.ysk.cashier.domain.QiniuContent;
import cn.ysk.cashier.service.QiNiuService;
import cn.ysk.cashier.service.WxService;
import cn.ysk.cashier.utils.CacheKey;
import cn.ysk.cashier.utils.RedisUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class WxServiceImpl implements WxService {
private final QiNiuService qiNiuService;
private final RedisUtils redisUtils;
private static final String APP_ID = "wxd88fffa983758a30";
private static final String APP_SECRET = "a34a61adc0602118b49400baa8812454";
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
private static final String QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacode";
@Override
public String getFetchQRCode(Map<String, Object> params) {
InputStream qrCodeStream = fetchQRCode(params);
QiniuContent qiniuContent = qiNiuService.uploadByte(IoUtil.readBytes(qrCodeStream),qiNiuService.findCloud());
redisUtils.set(CacheKey.VIPCODE + params.get("shopId"),qiniuContent.getUrl());
return qiniuContent.getUrl();
}
@Override
public String getSmallQrcode(String shopId) {
InputStream shopQRCodeStream = smallQrcode(shopId);
QiniuContent qiniuContent = qiNiuService.uploadByte(IoUtil.readBytes(shopQRCodeStream),qiNiuService.findCloud());
return qiniuContent.getUrl();
}
public InputStream fetchQRCode(Map<String, Object> params) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("path", "pages/member/index?shopId="+params.get("shopId"));//路径
jsonObject.addProperty("is_hyaline", true);//是否需要透明底色 true 生成透明底色的小程序码
if (params.containsKey("env_version")) jsonObject.addProperty("env_version", "trial");//正式版为 release体验版为 trial开发版为 develop
String accessToken = getAccessToken();
String url = String.format("%s?access_token=%s", QR_CODE_URL, accessToken);
return HttpUtil.createPost(url)
.body(jsonObject.toString(), "application/json")
.execute()
.bodyStream();
}
public InputStream smallQrcode(String shopId) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("path", "pages/index/index?shopId="+shopId);//路径
jsonObject.addProperty("is_hyaline", true);//是否需要透明底色 true 生成透明底色的小程序码
String accessToken = getAccessToken();
String url = String.format("%s?access_token=%s", QR_CODE_URL, accessToken);
return HttpUtil.createPost(url)
.body(jsonObject.toString(), "application/json")
.execute()
.bodyStream();
}
private String getAccessToken() {
String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s", TOKEN_URL, APP_ID, APP_SECRET);
String response = HttpUtil.get(url);
JSONObject jsonResponse = JSONObject.parseObject(response);
if (!jsonResponse.containsKey("access_token")) {
throw new RuntimeException("Failed to retrieve access token: " + response);
}
return jsonResponse.getString("access_token");
}
}

View File

@ -0,0 +1,399 @@
package cn.ysk.cashier.service.impl.app;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.ysk.cashier.cons.RedisConstant;
import cn.ysk.cashier.dto.calltable.*;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.entity.TbCallQueue;
import cn.ysk.cashier.mybatis.entity.TbCallTable;
import cn.ysk.cashier.mybatis.mapper.TbCallQueueMapper;
import cn.ysk.cashier.mybatis.mapper.TbShopUserMapper;
import cn.ysk.cashier.mybatis.service.TbCallQueueService;
import cn.ysk.cashier.mybatis.service.TbCallTableService;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.pojo.shop.TbShopUser;
import cn.ysk.cashier.repository.shop.TbShopInfoRepository;
import cn.ysk.cashier.service.app.TbCallService;
import cn.ysk.cashier.utils.Utils;
import cn.ysk.cashier.utils.WxMiniUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class TbCallServiceImpl implements TbCallService {
private final TbCallTableService callTableService;
private final TbCallQueueService callQueueService;
private final TbShopUserMapper shopUserMapper;
private final TbShopInfoRepository shopInfoRepository;
private final StringRedisTemplate redisTemplate;
private final TbCallQueueMapper tbCallQueueMapper;
private final WxMiniUtils wxMiniUtils;
@Override
public Object add(CallTableDTO addCallTableDTO) {
Long count = callTableService.lambdaQuery()
.eq(TbCallTable::getShopId, addCallTableDTO.getShopId())
.and(q -> {
q.eq(TbCallTable::getName, addCallTableDTO.getName())
.or()
.eq(TbCallTable::getPrefix, addCallTableDTO.getPrefix());
}).count();
if (count > 0) {
throw new BadRequestException("名称或前缀已存在");
}
TbCallTable callTable = BeanUtil.copyProperties(addCallTableDTO, TbCallTable.class);
callTable.setCreateTime(DateUtil.date().toInstant());
return callTableService.save(callTable);
}
@Override
public Object update(UpdateCallTableDTO callTableDTO) {
TbCallTable callTable = callTableService.lambdaQuery()
.eq(TbCallTable::getShopId, callTableDTO.getShopId())
.eq(TbCallTable::getId, callTableDTO.getCallTableId()).one();
if (callTable == null) {
throw new BadRequestException("桌型不存在");
}
TbCallTable newInfo = BeanUtil.copyProperties(callTableDTO, TbCallTable.class);
newInfo.setId(callTable.getId());
newInfo.setUpdateTime(DateUtil.date().toInstant());
return callTableService.updateById(newInfo);
}
@Override
public Object delete(BaseCallTableDTO baseCallTableDTO) {
return callTableService.remove(new LambdaQueryWrapper<TbCallTable>()
.eq(TbCallTable::getId, baseCallTableDTO.getCallTableId())
.eq(TbCallTable::getShopId, baseCallTableDTO.getShopId()));
}
private String getCallNumber(Integer shopId, TbCallTable callTable) {
return Utils.runFunAndCheckKey(() -> {
String callNumKey = RedisConstant.getTableCallNumKey(shopId, callTable.getId());
String value = redisTemplate.opsForValue().get(callNumKey);
AtomicReference<String> newVal = new AtomicReference<>("");
// 初始化
if (StrUtil.isBlank(value)) {
Boolean setFlag = Utils.runFunAndRetry(() -> redisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag,
r -> newVal.set(redisTemplate.opsForValue().get(callNumKey)));
if (setFlag) {
return callTable.getPrefix() + callTable.getStart();
}else if (StrUtil.isNotBlank(newVal.get())){
value = String.valueOf((Integer.parseInt(newVal.get()) + 1));
redisTemplate.opsForValue().set(callNumKey, value);
return callTable.getPrefix() + value;
}else {
throw new BadRequestException("生成排队号码失败");
}
}else {
value = String.valueOf((Integer.parseInt(value) + 1));
redisTemplate.opsForValue().set(callNumKey, value);
return callTable.getPrefix() + value;
}
}, redisTemplate, RedisConstant.getLockKey("UPDATE_TABLE", shopId, callTable.getId()));
}
@Override
public Object takeNumber(TakeNumberDTO takeNumberDTO) {
TbShopInfo shopInfo = shopInfoRepository.findById(takeNumberDTO.getShopId()).orElse(null);
if (shopInfo == null) {
throw new BadRequestException("店铺信息不存在");
}
TbCallTable callTable = callTableService.lambdaQuery()
.eq(TbCallTable::getShopId, takeNumberDTO.getShopId())
.eq(TbCallTable::getId, takeNumberDTO.getCallTableId()).one();
if (callTable == null) {
throw new BadRequestException("桌型不存在");
}
// 查询当前
// 拿取系统内部用户信息
TbCallQueue callQueue;
if (takeNumberDTO.getUserId() != null) {
TbShopUser shopUser = shopUserMapper.selectOne(new LambdaQueryWrapper<TbShopUser>()
.eq(TbShopUser::getStatus, 1)
.eq(TbShopUser::getShopId, takeNumberDTO.getShopId())
.eq(TbShopUser::getId, takeNumberDTO.getUserId()));
if (shopUser == null) {
throw new BadRequestException("用户不存在");
}
callQueue = callQueueService.lambdaQuery()
.eq(TbCallQueue::getUserId, shopUser.getId())
.eq(TbCallQueue::getShopId, takeNumberDTO.getShopId())
.in(TbCallQueue::getState, 0, 1)
.eq(TbCallQueue::getCallTableId, takeNumberDTO.getCallTableId()).one();
if (callQueue != null) {
throw new BadRequestException("当前用户已取号");
}
callQueue = BeanUtil.copyProperties(takeNumberDTO, TbCallQueue.class);
callQueue.setPhone(StrUtil.isBlank(takeNumberDTO.getPhone()) ? shopUser.getTelephone() : takeNumberDTO.getPhone());
// callQueue.setOpenId(shopUser.getMiniOpenId());
}else {
// if (StrUtil.isBlank(takeNumberDTO.getPhone()) || StrUtil.isBlank(takeNumberDTO.getOpenId())) {
// throw new BadRequestException("手机号或openId不能为空");
// }
callQueue = callQueueService.lambdaQuery()
.eq(TbCallQueue::getPhone, takeNumberDTO.getPhone())
.eq(TbCallQueue::getShopId, takeNumberDTO.getShopId())
.eq(TbCallQueue::getCallTableId, takeNumberDTO.getCallTableId()).one();
if (callQueue != null) {
throw new BadRequestException("当前用户已取号");
}
callQueue = BeanUtil.copyProperties(takeNumberDTO, TbCallQueue.class);
callQueue.setPhone(takeNumberDTO.getPhone());
// callQueue.setOpenId(takeNumberDTO.getOpenId());
callQueue.setSubState(1);
}
callQueue.setCallNum(getCallNumber(takeNumberDTO.getShopId(), callTable));
callQueue.setCreateTime(DateUtil.date().toInstant());
callQueue.setShopId(shopInfo.getId());
callQueue.setShopName(shopInfo.getShopName());
return callQueueService.save(callQueue);
}
@Override
public Object call(CallQueueDTO callQueueDTO) {
TbCallQueue callQueue = callQueueService.lambdaQuery()
.notIn(TbCallQueue::getState, -1, 2)
.eq(TbCallQueue::getShopId, callQueueDTO.getShopId())
.one();
if (callQueue == null) {
throw new BadRequestException("叫号用户不存在");
}
if (callQueue.getSubState().equals(0)) {
throw new BadRequestException("当前用户未订阅微信提醒");
}
callQueue.setState((byte) 1);
callQueue.setCallCount(callQueue.getCallCount() + 1);
callQueue.setCallTime(DateUtil.date().toInstant());
// 发送模板消息通知用户
TbShopInfo shopInfo = shopInfoRepository.findById(callQueue.getShopId()).orElse(null);
if (shopInfo == null) {
throw new BadRequestException("店铺信息不存在");
}
List<TbCallQueue> current = callQueueService.lambdaQuery()
.eq(TbCallQueue::getCallTableId, callQueue.getCallTableId())
.and(r -> {
r.eq(TbCallQueue::getState, 1)
.or()
.eq(TbCallQueue::getState, 0);
})
.orderByAsc(TbCallQueue::getCreateTime)
.page(new Page<>(1, 1)).getRecords();
if (StrUtil.isBlank(callQueue.getOpenId())) {
throw new BadRequestException("此用户未订阅微信小程序消息");
}
wxMiniUtils.sendNearCallMsg(shopInfo.getShopName(), getStrByState(Integer.valueOf(callQueue.getState())),
callQueue.getCallNum(), current.isEmpty() ? "" : current.get(0).getCallNum(), "排号信息", callQueue.getOpenId());
return callQueueService.updateById(callQueue);
}
private String getStrByState(Integer state) {
String msg = "";
switch (state) {
case -1:
msg = "已取消";
break;
case 0:
msg = "排队中";
break;
case 1:
msg = "已到号";
break;
case 3:
msg = "已过号";
}
return msg;
}
@Override
public Object updateInfo(UpdateCallQueueDTO updateCallQueueDTO) {
TbCallQueue callQueue = callQueueService.lambdaQuery()
.eq(TbCallQueue::getShopId, updateCallQueueDTO.getShopId())
.eq(TbCallQueue::getId, updateCallQueueDTO.getCallQueueId()).one();
if (callQueue == null) {
throw new BadRequestException("叫号用户不存在");
}
switch (updateCallQueueDTO.getState()) {
case -1:
callQueue.setCancelTime(DateUtil.date().toInstant());
break;
case 0:
if (callQueue.getSubState().equals(0)) {
throw new BadRequestException("当前用户未订阅微信提醒");
}
callQueue.setState((byte) 1);
callQueue.setCallCount(callQueue.getCallCount() + 1);
callQueue.setCallTime(DateUtil.date().toInstant());
break;
case 2:
callQueue.setConfirmTime(DateUtil.date().toInstant());
break;
case 3:
callQueue.setPassTime(DateUtil.date().toInstant());
break;
default:
throw new BadRequestException("错误类型");
}
callQueue.setState(updateCallQueueDTO.getState());
return callQueueService.updateById(callQueue);
}
@Override
public Object get(Integer page, Integer size, Integer shopId, Integer callTableId, Integer state) {
LambdaQueryWrapper<TbCallTable> query = new LambdaQueryWrapper<TbCallTable>()
.eq(TbCallTable::getShopId, shopId)
.eq(TbCallTable::getState, 1);
if (callTableId != null) {
query.eq(TbCallTable::getId, callTableId);
}
if (state != null) {
query.eq(TbCallTable::getState, state);
}
Page<TbCallTable> pageInfo = callTableService.page(new Page<>(page, size), query);
ArrayList<Map<String, Object>> info = new ArrayList<>();
pageInfo.getRecords().forEach(item -> {
Long count = callQueueService.lambdaQuery()
.eq(TbCallQueue::getCallTableId, item.getId())
.in(TbCallQueue::getState, 0, 1)
.count();
Map<String, Object> map = BeanUtil.beanToMap(item, false, false);
map.put("totalCount", count);
info.add(map);
});
Map<String, Object> toMap = BeanUtil.beanToMap(pageInfo);
toMap.put("records", info);
return toMap;
}
@Override
public Object takeNumberCode(Integer shopId, Integer callTableId) {
// 创建二维码配置对象设置宽度和高度为400
QrConfig config = new QrConfig(400, 400);
// 使用字节数组输出流来存储二维码图片
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 生成二维码图片输出到字节数组输出流
QrCodeUtil.generate("Your QR Code Content Here", config, "png", outputStream);
// 将图片转换为 Base64 字符串
String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());
// 返回Base64格式的图片字符串
return "data:image/png;base64," + base64;
}
@Override
public Object getQueue(Integer shopId, Integer callTableId, Integer state, Integer page, Integer size) {
List<Integer> tableIds;
if (callTableId != null) {
tableIds = Collections.singletonList(callTableId);
}else {
List<TbCallTable> list = callTableService.lambdaQuery()
.eq(TbCallTable::getShopId, shopId)
.eq(TbCallTable::getState, 1).list();
if (list.isEmpty()) {
return new Page<>();
}
tableIds = list.stream()
.map(TbCallTable::getId)
.collect(Collectors.toList());
}
LambdaQueryChainWrapper<TbCallQueue> query = callQueueService.lambdaQuery()
.eq(TbCallQueue::getShopId, shopId)
.in(TbCallQueue::getCallTableId, tableIds);
if (state != null) {
query.eq(TbCallQueue::getState, state);
}
Page<TbCallQueue> pageInfo = query
.orderByAsc(TbCallQueue::getCreateTime)
.orderByDesc(TbCallQueue::getState)
.page(new Page<>(page, size));
List<TbCallQueue> list1 = pageInfo.getRecords();
// 创建返回的结果集
List<Map<String, Object>> resultList = new ArrayList<>();
// 遍历每一个叫号中的记录计算前面状态为"0" (排队中) 的人数
for (TbCallQueue calling : list1) {
// 计算前面等待的人数 (状态为"0"且在叫号记录创建时间之前的)
long waitingCount = 0;
if (calling.getState() == 0) {
waitingCount = list1.stream()
.filter(item -> item.getState() == 0 || item.getState() == 1) // 过滤出状态为"排队中"
.filter(item -> item.getCreateTime().compareTo(calling.getCreateTime()) < 0 ) // 时间在当前叫号之前
.count();
}
// 创建一个Map来保存叫号中的记录及其前面排队的人数
Map<String, Object> map = BeanUtil.beanToMap(calling, false, false);
map.put("waitingCount", waitingCount);
// 将该map加入结果集
resultList.add(map);
}
Map<String, Object> toMap = BeanUtil.beanToMap(pageInfo, false, false);
toMap.put("records", resultList);
// 返回结果列表
return toMap;
}
@Override
public Object getCallRecord(Integer shopId, Integer callTableId, Integer page, Integer size) {
return tbCallQueueMapper.selectCallRecord(shopId, callTableId, new Page<>(page, size));
}
}

View File

@ -28,6 +28,7 @@ import cn.ysk.cashier.pojo.shop.TbPlussShopStaff;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.repository.TbShopPayTypeRepository;
import cn.ysk.cashier.repository.shop.*;
import cn.ysk.cashier.service.WxService;
import cn.ysk.cashier.service.shop.TbShopInfoService;
import cn.ysk.cashier.system.domain.Dept;
import cn.ysk.cashier.system.domain.Job;
@ -84,6 +85,7 @@ public class TbShopInfoServiceImpl implements TbShopInfoService {
private final RedisUtils redisUtils;
private final TbShopPayTypeRepository tbShopPayTypeRepository;
private final WxService wxService;
@Override
public Map<String,Object> queryAll(TbShopInfoQueryCriteria criteria){
@ -102,6 +104,11 @@ public class TbShopInfoServiceImpl implements TbShopInfoService {
@Transactional
public TbShopInfoDto findById(Integer id) {
TbShopInfo tbShopInfo = tbShopInfoRepository.findById(id).orElseGet(TbShopInfo::new);
if(StringUtils.isBlank(tbShopInfo.getSmallQrcode())){
String smallQrcode = wxService.getSmallQrcode(id.toString());
tbShopInfo.setSmallQrcode(smallQrcode);
tbShopInfoRepository.save(tbShopInfo);
}
ValidationUtil.isNull(tbShopInfo.getId(),"TbShopInfo","id",id);
return tbShopInfoMapper.toDto(tbShopInfo);
}
@ -239,7 +246,7 @@ public class TbShopInfoServiceImpl implements TbShopInfoService {
@Transactional(rollbackFor = Exception.class)
public void update(TbShopInfo resources) {
TbShopInfo tbShopInfo = tbShopInfoRepository.findById(resources.getId()).orElseGet(TbShopInfo::new);
if (!resources.getShopName().equals(tbShopInfo.getShopName())) {
if (StringUtils.isNotBlank(resources.getShopName()) && !resources.getShopName().equals(tbShopInfo.getShopName())) {
shopStaffRepository.updateNameById(resources.getShopName(),resources.getId().toString());
userRepository.updateNickName(resources.getAccount(),resources.getShopName());
}

View File

@ -18,6 +18,7 @@ package cn.ysk.cashier.service.impl.shopimpl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.ysk.cashier.config.security.security.TokenProvider;
import cn.ysk.cashier.cons.RedisConstant;
@ -28,12 +29,15 @@ import cn.ysk.cashier.enums.TableStateEnum;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.entity.TbShopOpenId;
import cn.ysk.cashier.mybatis.mapper.*;
import cn.ysk.cashier.mybatis.service.MpShopTableService;
import cn.ysk.cashier.pojo.TbShopPayType;
import cn.ysk.cashier.pojo.order.TbCashierCart;
import cn.ysk.cashier.pojo.order.TbOrderDetail;
import cn.ysk.cashier.pojo.order.TbOrderInfo;
import cn.ysk.cashier.pojo.product.TbProduct;
import cn.ysk.cashier.pojo.product.TbProductSku;
import cn.ysk.cashier.pojo.shop.TbMerchantAccount;
import cn.ysk.cashier.pojo.shop.TbShopArea;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import cn.ysk.cashier.repository.TbShopPayTypeRepository;
@ -44,7 +48,6 @@ import cn.ysk.cashier.repository.shop.TbShopInfoRepository;
import cn.ysk.cashier.service.impl.TbPayServiceImpl;
import cn.ysk.cashier.utils.*;
import cn.ysk.cashier.vo.PendingCountVO;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -66,11 +69,13 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.util.*;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
@ -105,6 +110,9 @@ public class TbShopTableServiceImpl implements TbShopTableService {
private final WxMsgUtils wxMsgUtils;
private final TbShopPayTypeRepository payTypeRepository;
private final MpShopTableMapper mpShopTableMapper;
private final TbShopPayTypeMapper tbShopPayTypeMapper;
private final MpShopTableService mpShopTableService;
/**
* 桌码前缀
@ -115,6 +123,7 @@ public class TbShopTableServiceImpl implements TbShopTableService {
private final TbCashierCartMapper tbCashierCartMapper;
private final TbOrderDetailMapper tbOrderDetailMapper;
private final StringRedisTemplate stringRedisTemplate;
private final MpShopAreaMapper mpShopAreaMapper;
@Override
public Map<String, Object> queryAll(TbShopTableQueryCriteria criteria, Pageable pageable) {
@ -147,13 +156,8 @@ public class TbShopTableServiceImpl implements TbShopTableService {
} else if (tbCashierCartMapper.selectCount(new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, date.getShopId())
.eq(TbCashierCart::getTableId, date.getQrcode())
.eq(TbCashierCart::getTradeDay, cn.ysk.cashier.utils.DateUtils.getDay())
.eq(TbCashierCart::getStatus, "create")) < 1 || (orderId != null &&
tbOrderDetailMapper.selectCount(new LambdaQueryWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getShopId, date.getShopId())
.eq(TbOrderDetail::getStatus, "unpaid")
.ge(TbOrderDetail::getCreateTime, DateUtil.beginOfDay(new Date()))
.eq(TbOrderDetail::getOrderId, orderId)) < 1)
.eq(TbCashierCart::getTradeDay, cn.ysk.cashier.utils.DateUtils.getDay())
.eq(TbCashierCart::getStatus, "create")) < 1
) {
date.setStatus("idle");
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
@ -161,6 +165,11 @@ public class TbShopTableServiceImpl implements TbShopTableService {
.set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
}
Map<String, Object> itemMap = BeanUtil.beanToMap(date, false, false);
if ((date.getStatus().equals("using") || date.getStatus().equals("cleaning")) && date.getUseTime() != null) {
itemMap.put("durationTime", DateUtil.current() - date.getUseTime().getTime());
}else {
itemMap.put("durationTime", 0);
}
if (!"".equals(date.getQrcode())) {
itemMap.put("qrcode", QRCODE + date.getQrcode().trim());
itemMap.put("tableId", date.getQrcode());
@ -224,6 +233,9 @@ public class TbShopTableServiceImpl implements TbShopTableService {
TbShopTable tbShopTable = tbShopTableRepository.findById(resources.getId()).orElseGet(TbShopTable::new);
ValidationUtil.isNull(tbShopTable.getId(), "TbShopTable", "id", resources.getId());
tbShopTable.copy(resources);
if ("idle".equals(resources.getStatus())) {
tbShopTable.setUseNum(0);
}
tbShopTableRepository.save(tbShopTable);
}
@ -265,6 +277,12 @@ public class TbShopTableServiceImpl implements TbShopTableService {
throw new BadRequestException("购物车商品不存在");
}
int currentPlaceNum = getCurrentPlaceNum(tbCashierCart.getTableId().toString(), tbCashierCart.getShopId());
if (tbCashierCart.getPlaceNum() != null && !tbCashierCart.getPlaceNum().equals(currentPlaceNum)) {
throw new BadRequestException("已下单商品仅支持退单操作");
}
if (updateCartDTO.getNum() == 0) {
cashierCartRepository.deleteById(updateCartDTO.getCartId());
return null;
@ -322,10 +340,25 @@ public class TbShopTableServiceImpl implements TbShopTableService {
return shopTable;
}
private int getCurrentPlaceNum(String tableId, String shopId) {
String currentOrderKey = RedisConstant.getCurrentOrderKey(tableId,
shopId);
String orderId = redisTemplate.opsForValue().get(currentOrderKey);
if (StrUtil.isBlank(orderId)) {
return 1;
}
TbOrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<TbOrderInfo>()
.eq(TbOrderInfo::getUseType, "postPay")
.eq(TbOrderInfo::getId, orderId).select(TbOrderInfo::getPlaceNum));
return orderInfo == null ? 1 : orderInfo.getPlaceNum() + 1;
}
@Override
public TbCashierCart addCartForUser(AddCartDTO addCartDTO) {
checkTableIsOpen(addCartDTO.getTableId());
int currentPlaceNum = getCurrentPlaceNum(addCartDTO.getTableId(), addCartDTO.getShopId().toString());
TbProductSku productSku = productMapper.selectSkuByIdAndShopId(addCartDTO.getShopId(), addCartDTO.getSkuId());
TbProduct product = productMapper.selectByIdAndShopId(addCartDTO.getShopId(), addCartDTO.getProductId());
@ -344,6 +377,7 @@ public class TbShopTableServiceImpl implements TbShopTableService {
.eq(TbCashierCart::getSkuId, addCartDTO.getSkuId())
.eq(TbCashierCart::getProductId, addCartDTO.getProductId())
.eq(TbCashierCart::getTableId, addCartDTO.getTableId())
.isNull(TbCashierCart::getPlaceNum)
.in(TbCashierCart::getStatus, "create", "refund")
// .and(q -> {
// q.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
@ -404,9 +438,11 @@ public class TbShopTableServiceImpl implements TbShopTableService {
tbCashierCart.setTotalNumber(addCartDTO.getNum());
tbCashierCart.setNumber(addCartDTO.getNum());
tbCashierCart.setCategoryId(product.getCategoryId());
tbCashierCart.setNote(addCartDTO.getNote());
cashierCartRepository.save(tbCashierCart);
} else {
tbCashierCart.setNote(addCartDTO.getNote());
tbCashierCart.setTotalAmount(new BigDecimal(addCartDTO.getNum()).multiply(productSku.getSalePrice()));
if (!addCartDTO.isPack()) {
@ -425,11 +461,6 @@ public class TbShopTableServiceImpl implements TbShopTableService {
tbCashierCart.setIsGift(String.valueOf(addCartDTO.isGift()));
tbCashierCart.setTotalNumber(addCartDTO.getNum());
tbCashierCart.setNumber(addCartDTO.getNum());
// 数量0删除
if (tbCashierCart.getNumber() == 0) {
cashierCartRepository.deleteById(tbCashierCart.getId());
return null;
}
tbCashierCart.setUpdatedAt(DateUtil.current());
cashierCartRepository.save(tbCashierCart);
}
@ -466,6 +497,7 @@ public class TbShopTableServiceImpl implements TbShopTableService {
@Override
public void removeCart(RemoveCartDTO removeCartDTO) {
int currentPlaceNum = getCurrentPlaceNum(removeCartDTO.getTableId().toString(), removeCartDTO.getShopId().toString());
// 会员点单
TbCashierCart cashierCart = cashierCartMapper.selectOne(new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, removeCartDTO.getShopId())
@ -474,16 +506,20 @@ public class TbShopTableServiceImpl implements TbShopTableService {
throw new BadRequestException("购物车商品不存在");
}
if (cashierCart.getPlaceNum() != null && !cashierCart.getPlaceNum().equals(currentPlaceNum)) {
throw new BadRequestException("已下单商品仅支持退单操作");
}
if (cashierCart.getOrderId() != null) {
orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getShopId, cashierCart.getShopId())
.eq(TbOrderDetail::getProductId, cashierCart.getProductId())
.eq(TbOrderDetail::getProductSkuId, cashierCart.getSkuId())
.isNull(TbOrderDetail::getPlaceNum)
.eq(TbOrderDetail::getOrderId, cashierCart.getOrderId()));
}
cashierCartMapper.delete(new LambdaQueryWrapper<TbCashierCart>().eq(TbCashierCart::getShopId, removeCartDTO.getShopId())
.eq(TbCashierCart::getId, removeCartDTO.getCartId()));
cashierCartMapper.deleteById(cashierCart.getId());
// 清空购物车 出票
long carCount = countCar(cashierCart.getTableId(), cashierCart.getShopId(), cashierCart.getMasterId());
@ -497,13 +533,56 @@ public class TbShopTableServiceImpl implements TbShopTableService {
}
@Override
public void returnCart(RemoveCartDTO removeCartDTO) {
rabbitMsgUtils.printDishesReturnTicket(4450, 9313);
int currentPlaceNum = getCurrentPlaceNum(removeCartDTO.getTableId().toString(), removeCartDTO.getShopId().toString());
// 会员点单
TbCashierCart cashierCart = cashierCartMapper.selectOne(new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, removeCartDTO.getShopId())
.in(TbCashierCart::getStatus, "create", "refund")
.eq(TbCashierCart::getId, removeCartDTO.getCartId()));
if (cashierCart == null) {
throw new BadRequestException("购物车商品不存在或已退单");
}
TbOrderDetail tbOrderDetail = orderDetailMapper.selectOne(new LambdaQueryWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getShopId, removeCartDTO.getShopId())
.eq(TbOrderDetail::getCartId, cashierCart.getId())
.eq(TbOrderDetail::getProductId, cashierCart.getProductId())
.eq(TbOrderDetail::getProductSkuId, cashierCart.getSkuId())
.eq(TbOrderDetail::getOrderId, cashierCart.getOrderId()));
if (tbOrderDetail == null) {
throw new BadRequestException("购物车商品不存在或已退单");
}
if (cashierCart.getOrderId() == null) {
throw new BadRequestException("此商品还未下单,无需退单");
}
cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
.eq(TbCashierCart::getId, cashierCart.getId())
.set(TbCashierCart::getStatus, "return"));
orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getId, tbOrderDetail.getId())
.set(TbOrderDetail::getUpdateTime, DateUtil.date())
.set(TbOrderDetail::getStatus, "return"));
rabbitMsgUtils.printDishesReturnTicket(tbOrderDetail.getOrderId(), tbOrderDetail.getOrderId());
}
@Override
public void clearCart(ClearCartDTO clearCartDTO) {
String orderId = redisTemplate.opsForValue().get(RedisConstant.getCurrentOrderKey(clearCartDTO.getTableId().toString(), clearCartDTO.getShopId()));
int currentPlaceNum = getCurrentPlaceNum(clearCartDTO.getTableId().toString(), clearCartDTO.getShopId());
cashierCartMapper.delete(new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, clearCartDTO.getShopId())
.eq(TbCashierCart::getTableId, clearCartDTO.getTableId())
.eq(TbCashierCart::getPlaceNum, currentPlaceNum)
.and(q -> {
q.eq(TbCashierCart::getMasterId, clearCartDTO.getMasterId())
.or()
@ -518,10 +597,13 @@ public class TbShopTableServiceImpl implements TbShopTableService {
})
);
// tbShopTableRepository.deleteByTableIdAndShopId(clearCartDTO.getTableId(), clearCartDTO.getShopId());
if (StrUtil.isNotBlank(orderId)) {
orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>().eq(TbOrderDetail::getShopId, clearCartDTO.getShopId())
orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getShopId, clearCartDTO.getShopId())
.eq(TbOrderDetail::getPlaceNum, currentPlaceNum)
.eq(TbOrderDetail::getOrderId, orderId));
rabbitMsgUtils.printTicket(orderId);
}
@ -537,11 +619,11 @@ public class TbShopTableServiceImpl implements TbShopTableService {
}
@Override
public com.baomidou.mybatisplus.extension.plugins.pagination.Page<TbCashierCart> getCart(Long tableId, Integer page,
Integer size, Integer shopId, Integer vipUserId, String masterId) {
public Map<String, Object> getCart(Long tableId, Integer page,
Integer size, Integer shopId, Integer vipUserId, String masterId) {
LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getTableId, tableId)
.in(TbCashierCart::getStatus, "create", "refund")
.in(TbCashierCart::getStatus, "create", "refund", "return")
.eq(TbCashierCart::getShopId, shopId)
.and(query2 -> {
query2.or(query3 -> {
@ -570,6 +652,8 @@ public class TbShopTableServiceImpl implements TbShopTableService {
skuIds.add(Integer.valueOf(item.getSkuId()));
});
AtomicReference<TbCashierCart> mealCashierCart = new AtomicReference<>();
if (!skuIds.isEmpty()) {
List<TbProductSku> skuList = productSkuRepository.findAllById(skuIds);
HashMap<String, TbProductSku> skuMap = new HashMap<>();
@ -577,17 +661,37 @@ public class TbShopTableServiceImpl implements TbShopTableService {
ArrayList<Map<String, Object>> infos = new ArrayList<>();
records.forEach(item -> {
if (item.getProductId().equals("-999")) {
mealCashierCart.set(item);
return;
}
Map<String, Object> map = BeanUtil.beanToMap(item, false, false);
TbProductSku tbProductSku = skuMap.get(item.getSkuId());
map.put("specSnap", tbProductSku != null ? tbProductSku.getSpecSnap() : null);
map.put("placeNum", item.getPlaceNum() == null ? 0 : item.getPlaceNum());
infos.add(map);
});
com.baomidou.mybatisplus.extension.plugins.pagination.Page copyPage = BeanUtil.copyProperties(cartPage, com.baomidou.mybatisplus.extension.plugins.pagination.Page.class);
copyPage.setRecords(infos);
return copyPage;
// 根据placeNum进行分组
Map<Object, List<Map<String, Object>>> groupedByPlaceNum = infos.stream()
.collect(Collectors.groupingBy(info -> info.get("placeNum")));
ArrayList<HashMap<String, Object>> list = new ArrayList<>();
groupedByPlaceNum.forEach((k, v) -> {
HashMap<String, Object> item = new HashMap<>();
item.put("placeNum", k);
item.put("info", v);
list.add(item);
});
copyPage.setRecords(list);
Map<String, Object> map = BeanUtil.beanToMap(copyPage, false, false);
map.put("seatFee", mealCashierCart);
return map;
}
return cartPage;
return BeanUtil.beanToMap(cartPage);
}
@Override
@ -835,7 +939,7 @@ public class TbShopTableServiceImpl implements TbShopTableService {
private final StringRedisTemplate redisTemplate;
@Override
public Object getMasterId(Integer shopId, Long tableId, Integer vipUserId) {
public JSONObject getMasterId(Integer shopId, Long tableId, Integer vipUserId) {
String account = tokenProvider.getSubject();
if (account == null) {
throw new BadRequestException("token解析失败");
@ -873,14 +977,34 @@ public class TbShopTableServiceImpl implements TbShopTableService {
@Override
public TbOrderInfo createOrder(CreateOrderDTO createOrderDTO, boolean addMaterId, boolean isPrint) {
public TbOrderInfo createOrder(CreateOrderDTO createOrderDTO, boolean addMaterId, boolean isPrint) {
return Utils.runFunAndCheckKey(() -> {
TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
.in(TbShopTable::getStatus, "idle", "using"));
if (tbShopTable == null) {
throw new BadRequestException("台桌未开台或不存在");
}
// 传递orderId直接取否则取当前缓存id
String currentOrderKey = RedisConstant.getCurrentOrderKey(createOrderDTO.getTableId(),
createOrderDTO.getShopId().toString());
String orderIdValue = redisTemplate.opsForValue().get(currentOrderKey);
Integer orderId = orderIdValue == null ? null : Integer.parseInt(orderIdValue);
orderId = createOrderDTO.getOrderId() != null ? createOrderDTO.getOrderId() : orderId;
// 查询订单
TbOrderInfo orderInfo = null;
if (orderId != null) {
orderInfo = orderInfoMapper.selectById(orderId);
if (orderInfo == null || !"unpaid".equals(orderInfo.getStatus())) {
redisTemplate.delete(currentOrderKey);
}
}
String day = DateUtils.getDay();
LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, createOrderDTO.getShopId())
@ -896,36 +1020,37 @@ public class TbShopTableServiceImpl implements TbShopTableService {
.isNull(TbCashierCart::getMasterId);
}));
});
// if (createOrderDTO.getVipUserId() != null) {
// queryWrapper.eq(TbCashierCart::getUserId, createOrderDTO.getVipUserId());
// }else {
// queryWrapper.eq(TbCashierCart::getMasterId, createOrderDTO.getMasterId())
// .isNull(TbCashierCart::getUserId);
// }
List<TbCashierCart> cashierCarts = cashierCartMapper
.selectList(queryWrapper);
if (cashierCarts.isEmpty()) {
throw new BadRequestException("购物车为空,请先添加商品");
}
TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
.in(TbShopTable::getStatus, "idle", "using"));
if (tbShopTable == null) {
throw new BadRequestException("台桌未开台或不存在");
}
BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal packAMount = BigDecimal.ZERO;
BigDecimal feeAmount = BigDecimal.ZERO;
BigDecimal saleAmount = BigDecimal.ZERO;
// 当前下单次数
int placeNum = getCurrentPlaceNum(createOrderDTO.getTableId(), createOrderDTO.getShopId().toString());
List<TbOrderDetail> orderDetails = new ArrayList<>();
Integer mealNum = null;
boolean unAdd = cashierCarts.stream().noneMatch(item -> item.getPlaceNum() == null);
if (addMaterId && unAdd) {
throw new BadRequestException("此次未添加新商品,清先添加商品");
}
for (TbCashierCart cashierCart : cashierCarts) {
totalAmount = totalAmount.add(cashierCart.getTotalAmount());
packAMount = packAMount.add(cashierCart.getPackFee());
feeAmount = cashierCart.getPackFee();
if ("-999".equals(cashierCart.getProductId())) {
mealNum = cashierCart.getNumber();
}
if (!"return".equals(cashierCart.getStatus())) {
totalAmount = totalAmount.add(cashierCart.getTotalAmount());
packAMount = packAMount.add(cashierCart.getPackFee());
feeAmount = cashierCart.getPackFee();
}
TbProductSku productSku = productSkuRepository.findById(Integer.valueOf(cashierCart.getSkuId())).orElse(null);
TbOrderDetail orderDetail = new TbOrderDetail();
@ -934,6 +1059,7 @@ public class TbShopTableServiceImpl implements TbShopTableService {
orderDetail.setProductSkuName(productSku.getSpecSnap());
}
orderDetail.setNote(cashierCart.getNote());
orderDetail.setCreateTime(DateUtil.date().toTimestamp());
orderDetail.setNum(cashierCart.getNumber());
orderDetail.setPrice(cashierCart.getSalePrice());
@ -945,20 +1071,17 @@ public class TbShopTableServiceImpl implements TbShopTableService {
orderDetail.setPackAmount(cashierCart.getPackFee());
orderDetail.setStatus("unpaid");
orderDetail.setProductImg(cashierCart.getCoverImg());
orderDetail.setCartId(cashierCart.getId());
orderDetails.add(orderDetail);
if (cashierCart.getOrderId() != null) {
orderId = cashierCart.getOrderId();
}
orderDetail.setOrderId(orderId);
orderDetail.setPlaceNum(placeNum);
}
TbOrderInfo orderInfo = null;
if (orderId != null) {
orderInfo = orderInfoMapper.selectById(orderId);
if (orderInfo == null || !"unpaid".equals(orderInfo.getStatus())) {
redisTemplate.delete(currentOrderKey);
}
if (mealNum == null) {
throw new BadRequestException("请选择用餐人数");
}
// 修改订单信息
@ -980,6 +1103,9 @@ public class TbShopTableServiceImpl implements TbShopTableService {
orderInfo.setUseType(createOrderDTO.isPostPay() ? "postPay" : "afterPay");
orderInfo.setUserId(createOrderDTO.getVipUserId() == null ? null : String.valueOf(createOrderDTO.getVipUserId()));
orderInfo.setCreatedAt(DateUtil.current());
if (!unAdd) {
orderInfo.setPlaceNum(placeNum);
}
orderInfoMapper.updateById(orderInfo);
} else {
String orderNo = generateOrderNumber();
@ -1006,6 +1132,7 @@ public class TbShopTableServiceImpl implements TbShopTableService {
orderInfo.setUserId(createOrderDTO.getVipUserId() == null ? null : String.valueOf(createOrderDTO.getVipUserId()));
orderInfo.setCreatedAt(DateUtil.current());
orderInfo.setTableName(tbShopTable.getName());
orderInfo.setPlaceNum(placeNum);
TbMerchantAccount merchantAccount = merchantAccountMapper.selectOne(new LambdaQueryWrapper<TbMerchantAccount>()
.eq(TbMerchantAccount::getShopId, createOrderDTO.getShopId())
.eq(TbMerchantAccount::getStatus, 1));
@ -1030,6 +1157,9 @@ public class TbShopTableServiceImpl implements TbShopTableService {
// 是否是第一次添加的商品
boolean isFirst = true;
for (TbCashierCart cashierCart : cashierCarts) {
if ("-999".equals(cashierCart.getProductId())) {
continue;
}
TbProduct product = productMapper.selectById(cashierCart.getProductId());
TbProductSku productSku = productSkuRepository.findById(Integer.valueOf(cashierCart.getSkuId())).orElse(null);
@ -1041,6 +1171,9 @@ public class TbShopTableServiceImpl implements TbShopTableService {
cashierCart.setOrderId(orderId);
cashierCart.setUpdatedAt(System.currentTimeMillis());
cashierCart.setStatus("pending".equals(orderInfo.getStatus()) ? "refund" : cashierCart.getStatus());
if (cashierCart.getPlaceNum() == null) {
cashierCart.setPlaceNum(placeNum);
}
cashierCartMapper.updateById(cashierCart);
}
if (isFirst) {
@ -1061,8 +1194,6 @@ public class TbShopTableServiceImpl implements TbShopTableService {
// 推送耗材信息
pushConsMsg(orderInfo, cashierCarts);
if (createOrderDTO.isPostPay() && isPrint) {
Long count = orderInfoMapper.selectCount(new LambdaQueryWrapper<TbOrderInfo>()
.eq(TbOrderInfo::getStatus, "unpaid")
@ -1072,15 +1203,24 @@ public class TbShopTableServiceImpl implements TbShopTableService {
}
}
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
LambdaUpdateWrapper<TbShopTable> wrapper = new LambdaUpdateWrapper<TbShopTable>()
.eq(TbShopTable::getShopId, createOrderDTO.getShopId())
.eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
.set(TbShopTable::getStatus, TableStateEnum.USING.getState()));
.set(TbShopTable::getProductNum, cashierCarts.size())
.set(TbShopTable::getTotalAmount, orderInfo.getOrderAmount())
.set(TbShopTable::getRealAmount, orderInfo.getOrderAmount())
.set(TbShopTable::getUseNum, mealNum)
.set(TbShopTable::getStatus, TableStateEnum.USING.getState());
if (isFirst) {
wrapper.set(TbShopTable::getUseTime, DateUtil.date());
}
// 设置台桌信息
mpShopTableMapper.update(null, wrapper);
String tableCartKey = RedisConstant.getTableCartKey(createOrderDTO.getTableId(), createOrderDTO.getShopId().toString());
redisTemplate.delete(tableCartKey);
return orderInfo;
}, stringRedisTemplate, RedisConstant.getLockKey(RedisConstant.CREATE_ORDER,createOrderDTO.getShopId(),
}, stringRedisTemplate, RedisConstant.getLockKey(RedisConstant.CREATE_ORDER, createOrderDTO.getShopId(),
createOrderDTO.getTableId(), createOrderDTO.getMasterId(), createOrderDTO.isPostPay()));
}
@ -1236,25 +1376,70 @@ public class TbShopTableServiceImpl implements TbShopTableService {
@Override
public Object pay(PayDTO payDTO) {
return Utils.runFunAndCheckKey(() -> {
TbOrderInfo orderInfo = null;
long count = tbShopPayTypeMapper.selectCount(new LambdaQueryWrapper<TbShopPayType>()
.eq(TbShopPayType::getShopId, payDTO.getShopId())
.eq(TbShopPayType::getIsDisplay, 1)
.eq(TbShopPayType::getPayType, "deposit"));
if (count < 1) {
throw new BadRequestException("未知支付方式");
}
TbOrderInfo orderInfo = orderInfoMapper.selectById(payDTO.getOrderId());
if (ObjectUtil.isEmpty(orderInfo)) {
throw new BadRequestException("订单不存在");
}
if (!"unpaid".equals(orderInfo.getStatus()) && !"pending".equals(orderInfo.getStatus())) {
throw new BadRequestException("订单非未支付状态");
}
if (payDTO.getDiscount() == null) {
payDTO.setDiscount((double) 1);
}
BigDecimal finalAmount = orderInfo.getOrderAmount().multiply(BigDecimal.valueOf(payDTO.getDiscount())).setScale(2, RoundingMode.HALF_UP);
switch (payDTO.getPayType()) {
case "vipPay":
orderInfo = tbPayServiceImpl.vipPay(payDTO.getShopId(), payDTO.getOrderId(), payDTO.getDiscount(), payDTO.getVipUserId());
if (payDTO.getVipUserId() != null) {
orderInfo.setUserId(String.valueOf(payDTO.getVipUserId()));
}
tbPayServiceImpl.vipPay(finalAmount, Integer.valueOf(orderInfo.getUserId()));
orderInfo.setPayOrderNo("vipPay".concat(SnowFlakeUtil.generateOrderNo()));
orderInfo.setPayType("deposit");
break;
case "cash":
orderInfo = tbPayServiceImpl.cashPay(payDTO);
orderInfo.setPayType("cash");
orderInfo.setPayOrderNo("cash".concat(SnowFlakeUtil.generateOrderNo()));
// orderInfo = tbPayServiceImpl.cashPay(payDTO);
break;
case "scanCode":
orderInfo = tbPayServiceImpl.scanPay(payDTO);
orderInfo = tbPayServiceImpl.scanPay(payDTO.getShopId(), payDTO.getCode(), Integer.valueOf(orderInfo.getMerchantId()), Integer.valueOf(orderInfo.getMemberId()), finalAmount, orderInfo);
break;
case "deposit":
orderInfo = tbPayServiceImpl.memberAccountPay("", String.valueOf(payDTO.getShopId()), payDTO.getCode(), payDTO.getOrderId(), payDTO.getDiscount());
orderInfo = tbPayServiceImpl.memberAccountPay("", String.valueOf(payDTO.getShopId()), payDTO.getCode(), orderInfo, finalAmount);
break;
default:
throw new BadRequestException("未知支付方式");
}
log.info("更新购物车");
orderInfo.setPayAmount(finalAmount);
orderInfo.setStatus("closed");
orderInfo.setDiscountRatio(BigDecimal.valueOf(payDTO.getDiscount()));
orderInfo.setDiscountAmount(orderInfo.getAmount().subtract(finalAmount));
orderInfoMapper.updateById(orderInfo);
//更新购物车状态
cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
.eq(TbCashierCart::getOrderId, orderInfo.getId())
.set(TbCashierCart::getStatus, "final"));
orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getOrderId, orderInfo.getId())
.set(TbOrderDetail::getStatus, "closed"));
JSONObject jsonObject = new JSONObject();
jsonObject.put("token", null);
@ -1272,9 +1457,21 @@ public class TbShopTableServiceImpl implements TbShopTableService {
rabbitTemplate.convertAndSend(RabbitConstants.EXCHANGE_STOCK_RECORD, RabbitConstants.ROUTING_STOCK_RECORD_SALE, mqData.toJSONString(), new CorrelationData(UUID.randomUUID().toString()));
// 修改台桌状态
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, orderInfo.getTableId())
.set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
.eq(TbShopTable::getShopId, payDTO.getShopId())
.eq(TbShopTable::getQrcode, orderInfo.getTableId()));
if (tbShopTable.getAutoClear().equals(1)) {
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, orderInfo.getTableId())
.set(TbShopTable::getEndTime, DateUtil.date())
.set(TbShopTable::getUseNum, 0)
.set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
} else {
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, orderInfo.getTableId())
.set(TbShopTable::getEndTime, DateUtil.date())
.set(TbShopTable::getStatus, TableStateEnum.CLEANING.getState()));
}
if ("postPay".equals(orderInfo.getUseType())) {
String day = DateUtils.getDay();
@ -1327,4 +1524,224 @@ public class TbShopTableServiceImpl implements TbShopTableService {
}
return cashierCartMapper.update(null, queryWrapper);
}
private String getCurrentOrderId(String tableId, String shopId) {
String currentOrderKey = RedisConstant.getCurrentOrderKey(tableId, shopId);
return redisTemplate.opsForValue().get(currentOrderKey);
}
private void setCurrentOrderId(String tableId, String shopId, String orderId) {
String currentOrderKey = RedisConstant.getCurrentOrderKey(tableId, shopId);
redisTemplate.opsForValue().set(currentOrderKey, orderId);
}
private String removeCurrentOrderId(String tableId, String shopId) {
String currentOrderKey = RedisConstant.getCurrentOrderKey(tableId, shopId);
String orderId = redisTemplate.opsForValue().get(currentOrderKey);
redisTemplate.delete(currentOrderKey);
return orderId;
}
@Override
public Object choseTable(ChoseTableDTO choseTableDTO) {
String masterId = getMasterId(choseTableDTO.getShopId(), Long.valueOf(choseTableDTO.getNewTableId()), null).getString("masterId");
String currentOrderId = choseTableDTO.getOrderId() == null ? getCurrentOrderId(choseTableDTO.getOldTableId(),
choseTableDTO.getShopId().toString()) : choseTableDTO.getOrderId().toString();
TbShopTable shopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, choseTableDTO.getNewTableId())
.eq(TbShopTable::getStatus, "idle"));
if (shopTable == null) {
throw new BadRequestException("台桌不存在或非空闲状态");
}
cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
.eq(TbCashierCart::getTableId, choseTableDTO.getOldTableId())
.in(TbCashierCart::getStatus, "create", "return")
.eq(TbCashierCart::getShopId, choseTableDTO.getShopId())
.and(q -> {
q.isNull(TbCashierCart::getOrderId)
.or()
.eq(TbCashierCart::getOrderId, currentOrderId);
})
.and(query2 -> {
query2.or(query3 -> {
query3.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
.isNotNull(TbCashierCart::getMasterId);
})
.or((query4 -> {
query4.isNull(TbCashierCart::getTradeDay)
.isNull(TbCashierCart::getMasterId);
}));
})
.set(TbCashierCart::getMasterId, masterId)
.set(TbCashierCart::getTableId, choseTableDTO.getNewTableId()));
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, choseTableDTO.getNewTableId())
.set(TbShopTable::getStatus, TableStateEnum.USING.getState()));
mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, choseTableDTO.getOldTableId())
.set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
// 将台桌redis数据迁移
String orderId = removeCurrentOrderId(choseTableDTO.getOldTableId(), choseTableDTO.getShopId().toString());
setCurrentOrderId(choseTableDTO.getNewTableId(), choseTableDTO.getShopId().toString(), orderId);
return orderInfoMapper.update(null, new LambdaUpdateWrapper<TbOrderInfo>()
.eq(TbOrderInfo::getId, currentOrderId)
.set(TbOrderInfo::getMasterId, masterId)
.set(TbOrderInfo::getTableId, choseTableDTO.getNewTableId()));
}
@Override
public Object choseCount(ChoseCountDTO choseCountDTO) {
TbShopTable shopTable = mpShopTableService.lambdaQuery().eq(TbShopTable::getQrcode, choseCountDTO.getTableId()).one();
if (shopTable == null) {
throw new BadRequestException("台桌不存在");
}
if (shopTable.getMaxCapacity() < choseCountDTO.getNum()) {
throw new BadRequestException("当前台桌最大人数为: " + shopTable.getMaxCapacity());
}
LambdaQueryWrapper<TbCashierCart> query = new LambdaQueryWrapper<TbCashierCart>()
.eq(TbCashierCart::getShopId, choseCountDTO.getShopId())
.eq(TbCashierCart::getMasterId, choseCountDTO.getMasterId())
.eq(TbCashierCart::getProductId, "-999")
.eq(TbCashierCart::getSkuId, "-999")
.eq(TbCashierCart::getTradeDay, cn.ysk.cashier.utils.DateUtils.getDay())
.eq(TbCashierCart::getTableId, choseCountDTO.getTableId());
TbCashierCart tbCashierCart = cashierCartMapper.selectOne(query);
if (tbCashierCart == null) {
tbCashierCart = new TbCashierCart();
tbCashierCart.setStatus("create");
tbCashierCart.setCreatedAt(System.currentTimeMillis());
tbCashierCart.setTableId(Long.valueOf(choseCountDTO.getTableId()));
tbCashierCart.setName("客座费");
tbCashierCart.setSalePrice(BigDecimal.ONE);
tbCashierCart.setMasterId(choseCountDTO.getMasterId());
tbCashierCart.setShopId(String.valueOf(choseCountDTO.getShopId()));
tbCashierCart.setTradeDay(DateUtils.getDay());
tbCashierCart.setStatus("create");
tbCashierCart.setTotalAmount(new BigDecimal(choseCountDTO.getNum()).multiply(BigDecimal.ONE));
tbCashierCart.setPlaceNum(1);
tbCashierCart.setProductId("-999");
tbCashierCart.setSkuId("-999");
tbCashierCart.setPackFee(BigDecimal.ZERO);
tbCashierCart.setNumber(choseCountDTO.getNum());
tbCashierCart.setTotalNumber(choseCountDTO.getNum());
tbCashierCartMapper.insert(tbCashierCart);
} else {
tbCashierCart.setTotalAmount(new BigDecimal(choseCountDTO.getNum()).multiply(BigDecimal.ONE));
tbCashierCart.setNumber(choseCountDTO.getNum());
tbCashierCart.setTotalNumber(choseCountDTO.getNum());
tbCashierCartMapper.updateById(tbCashierCart);
}
return tbCashierCart;
}
@Override
public Object printOrder(BaseTableDTO baseTableDTO) {
checkTableState(baseTableDTO.getTableId());
String currentOrderId = getCurrentOrderId(baseTableDTO.getTableId(), baseTableDTO.getShopId().toString());
if (StrUtil.isBlank(currentOrderId)) {
throw new BadRequestException("当前台桌不存在订单");
}
TbOrderInfo orderInfo = orderInfoMapper.selectById(currentOrderId);
rabbitMsgUtils.printPlaceTicket(orderInfo.getId(), false);
return true;
}
@Override
public Object generate(TableGenerateDTO generateDTO) {
TbShopArea shopArea = mpShopAreaMapper.selectOne(new LambdaQueryWrapper<TbShopArea>()
.eq(TbShopArea::getShopId, generateDTO.getShopId())
.eq(TbShopArea::getId, generateDTO.getAreaId()));
if (shopArea == null) {
throw new BadRequestException("台桌区域不存在");
}
List<TbShopTable> tbShopTables = mpShopTableMapper.selectList(new LambdaQueryWrapper<TbShopTable>()
.eq(TbShopTable::getShopId, generateDTO.getShopId())
.select(TbShopTable::getName));
HashMap<String, Object> tableMap = new HashMap<>();
tbShopTables.forEach(item -> {
tableMap.put(item.getName(), item);
});
StringBuilder msg = new StringBuilder();
ArrayList<TbShopTable> tableArrayList = new ArrayList<>();
for (int i = generateDTO.getStart(); i <= generateDTO.getEnd(); i++) {
String name = generateDTO.getSign() + i;
if (tableMap.get(name) != null) {
msg.append(name).append(";");
continue;
}
TbShopTable shopTable = new TbShopTable();
shopTable.setShopId(generateDTO.getShopId());
shopTable.setName(name);
shopTable.setMaxCapacity(generateDTO.getCapacity());
shopTable.setAreaId(generateDTO.getAreaId());
shopTable.setCreatedAt(DateUtil.current());
shopTable.setStatus(TableStateEnum.CLOSED.getState());
tableArrayList.add(shopTable);
}
mpShopTableService.saveBatch(tableArrayList);
if (StrUtil.isNotBlank(msg.toString())) {
msg.append("台桌名已存在,未添加");
throw new BadRequestException(msg.toString());
}
return true;
}
private TbShopTable checkTableState(String tableId) {
TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
.eq(TbShopTable::getQrcode, tableId)
.in(TbShopTable::getStatus, TableStateEnum.USING.getState(), TableStateEnum.PAYING.getState(), TableStateEnum.CLEANING.getState()));
if (tbShopTable == null) {
throw new BadRequestException("台桌不存在或当前台桌未开台");
}
return tbShopTable;
}
private TbOrderInfo getCurrentOrder(String tableId, String shopId) {
String currentOrderId = getCurrentOrderId(tableId, shopId);
if (StrUtil.isBlank(currentOrderId)) {
throw new BadRequestException("当前台桌不存在订单");
}
TbOrderInfo orderInfo = orderInfoMapper.selectById(currentOrderId);
if (orderInfo == null) {
throw new BadRequestException("当前台桌不存在订单");
}
return orderInfo;
}
@Override
public Object printDishes(BaseTableDTO baseTableDTO) {
checkTableState(baseTableDTO.getTableId());
TbOrderInfo currentOrder = getCurrentOrder(baseTableDTO.getTableId(), baseTableDTO.getShopId().toString());
List<TbOrderDetail> detailList = orderDetailMapper.selectList(new LambdaQueryWrapper<TbOrderDetail>()
.eq(TbOrderDetail::getOrderId, currentOrder.getId())
.eq(TbOrderDetail::getStatus, "unpaid")
.ne(TbOrderDetail::getProductId, -999)
.select(TbOrderDetail::getId));
if (detailList.isEmpty()) {
throw new BadRequestException("当前台桌还未下单任何菜品, 请先下单");
}
Integer[] detailIds = new Integer[detailList.size()];
for (int i = 0; i < detailList.size(); i++) {
detailIds[i] = detailList.get(i).getId();
}
rabbitMsgUtils.printDishesTicket(currentOrder.getId(), false, detailIds);
return true;
}
}

View File

@ -20,7 +20,7 @@ import cn.ysk.cashier.pojo.order.TbCashierCart;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import cn.ysk.cashier.dto.shop.TbShopTableDto;
import cn.ysk.cashier.dto.shop.TbShopTableQueryCriteria;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.alibaba.fastjson.JSONObject;
import org.springframework.data.domain.Pageable;
import java.util.Map;
import java.util.List;
@ -100,9 +100,11 @@ public interface TbShopTableService {
void removeCart(RemoveCartDTO removeCartDTO);
void returnCart(RemoveCartDTO removeCartDTO);
void clearCart(ClearCartDTO clearCartDTO);
Page<TbCashierCart> getCart(Long tableId, Integer page, Integer size, Integer shopId, Integer vipUserId, String masterId);
Map<String, Object> getCart(Long tableId, Integer page, Integer size, Integer shopId, Integer vipUserId, String masterId);
TbCashierCart updateCart(UpdateCartDTO updateCartDTO);
@ -110,7 +112,7 @@ public interface TbShopTableService {
Object createOrder(CreateOrderDTO createOrderDTO, boolean addMasterId, boolean isPrint);
Object getMasterId(Integer shopId, Long tableId, Integer vipUserId);
JSONObject getMasterId(Integer shopId, Long tableId, Integer vipUserId);
Object pending(PendingDTO pendingDTO);
@ -123,4 +125,14 @@ public interface TbShopTableService {
Object deleteOrder(DeleteOrderDTO deleteOrderDTO);
Object updateVip(UpdateVipDTO updateVipDTO);
Object choseTable(ChoseTableDTO choseTableDTO);
Object choseCount(ChoseCountDTO choseCountDTO);
Object printOrder(BaseTableDTO baseTableDTO);
Object generate(TableGenerateDTO generateDTO);
Object printDishes(BaseTableDTO baseTableDTO);
}

View File

@ -46,4 +46,28 @@ public class RabbitMsgUtils implements RabbitTemplate.ConfirmCallback {
// 发送库存记录mq消息
sendMsg(RabbitConstants.EXCHANGE_STOCK_RECORD, RabbitConstants.ROUTING_STOCK_RECORD_SALE, mqData, "库存记录", true);
}
public void printDishesReturnTicket(Integer orderId, Integer... orderIds) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("orderId", orderId);
jsonObject.put("orderDetailIds", orderIds);
jsonObject.put("isReturn", true);
sendMsg(RabbitConstants.EXCHANGE_PRINT, RabbitConstants.ROUTING_KEY_PRINT_DISHES, jsonObject, "菜品退单", false);
}
public void printDishesTicket(Integer orderId, boolean isReturn, Integer... detailOrderIds) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("orderId", orderId);
jsonObject.put("orderDetailIds", detailOrderIds);
jsonObject.put("isReturn", isReturn);
sendMsg(RabbitConstants.EXCHANGE_PRINT, RabbitConstants.ROUTING_KEY_PRINT_DISHES, jsonObject, "菜品打印", false);
}
public void printPlaceTicket(Integer id, boolean isReturn) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("orderId", id);
jsonObject.put("isReturn", isReturn);
sendMsg(RabbitConstants.EXCHANGE_PRINT, RabbitConstants.ROUTING_KEY_PRINT_PLACE, jsonObject, "订单打印", false);
}
}

View File

@ -156,4 +156,7 @@ public class WxAccountUtil {
}
}
}

View File

@ -0,0 +1,113 @@
package cn.ysk.cashier.utils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Component
@Slf4j
public class WxMiniUtils {
@Value("${wx.mini.user.appId}")
private String appId;
@Value("${wx.mini.user.secrete}")
private String secrete;
@Value("${wx.mini.user.msgId.currentCall}")
private String currentCallTempId;
static LinkedHashMap<String,String> linkedHashMap=new LinkedHashMap<>();
static {
linkedHashMap.put("40001","获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口");
linkedHashMap.put("40003","不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID");
linkedHashMap.put("40014","不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口");
linkedHashMap.put("40037","不合法的 template_id");
linkedHashMap.put("43101","用户未订阅消息");
linkedHashMap.put("43107","订阅消息能力封禁");
linkedHashMap.put("43108","并发下发消息给同一个粉丝");
linkedHashMap.put("45168","命中敏感词");
linkedHashMap.put("47003","参数错误");
}
public JSONObject getAccessToken(){
String requestUrl = "https://api.weixin.qq.com/cgi-bin/token";
Map<String, Object> requestUrlParam = new HashMap<>();
//小程序appId
requestUrlParam.put("appid", appId);
//小程序secret
requestUrlParam.put("secret", secrete);
//默认参数
requestUrlParam.put("grant_type", "client_credential");
return JSON.parseObject(HttpUtil.get(requestUrl,requestUrlParam));
}
public static void main(String[] args) {
String id ="kSxJL9TR4s_UmOmNLE";
// sendStockWarnMsg("123", "1231", "1231", "23321", id);
}
public JSONObject sendTempMsg(String tempId, String toUserOpenId, Map<String, Object> data) {
log.info("开始发送微信模板消息, 接收用户openId: {}, 消息数据: {}", toUserOpenId, data);
JSONObject object= getAccessToken();
String accessToken=object.get("access_token")+"";
JSONObject object1=new JSONObject();
object1.put("template_id", tempId);
object1.put("touser", toUserOpenId);
object1.put("data",data);
object1.put("miniprogram_state","trial");
object1.put("lang","zh_CN");
String response= HttpRequest.post("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=".concat(accessToken)).body(object1.toString()).execute().body();
log.info("微信模板消息发送成功,相应内容:{}",response);
JSONObject resObj=JSONObject.parseObject(response);
if(ObjectUtil.isNotEmpty(resObj)&&ObjectUtil.isNotNull(resObj)&&"0".equals(resObj.get("errcode")+"")){
return resObj;
}
throw new RuntimeException(linkedHashMap.getOrDefault(resObj.get("errcode") + "", "未知错误"));
}
public void sendNearCallMsg(String shopName, String state, String callNum, String currentNum, String note, String openId) {
Map<String, Object> data = new HashMap<String, Object>() {{
put("thing1", new HashMap<String, Object>() {{
put("value", shopName);
}});
put("phrase2", new HashMap<String, Object>() {{
put("value", state);
}});
put("character_string3", new HashMap<String, Object>() {{
put("value", callNum);
}});
put("character_string4", new HashMap<String, Object>() {{
put("value", currentNum);
}});
put("thing5", new HashMap<String, Object>() {{
put("value", note);
}});
}};
log.info("开始发送排号到号消息, 接收用户openId: {}, 消息数据: {}", openId, data);
try {
sendTempMsg(currentCallTempId, openId, data);
} catch (Exception e) {
log.error("发送失败, openId:{}, msg: {}", openId, e.getMessage());
}
}
}

View File

@ -88,8 +88,18 @@ wx:
operationMsgTmpId: wFdoUG-dUT7bDRHq8bMJD9CF5TjyH9x_uJQgQByZqHg
warnMsgTmpId: C08OUr80x6wGmUN1zpFhSQ3Sv7VF5vksdZigiEx2pD0
mini:
user:
appId: wxd88fffa983758a30
secrete: a34a61adc0602118b49400baa8812454
msgId:
nearCall: yxOjWK-KjMEZ BaHWgDJJpHiUPXN6JWgr7u9y65RIWM
currentCall: 3BgFazRpVlvreh5z9u4cNPVeclXKSQfh-r3x2 bYx4
gateway:
url: https://gateway.api.sxczgkj.cn/gate-service/
client:
backUrl: https://cashierclient.sxczgkj.cn/cashier-client/notify/notifyPay
logging:
level:
root: error

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.ysk.cashier.mybatis.mapper.TbCallQueueMapper">
<resultMap id="BaseResultMap" type="cn.ysk.cashier.mybatis.entity.TbCallQueue">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="call_table_id" column="call_table_id" jdbcType="INTEGER"/>
<result property="phone" column="phone" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="shop_name" column="shop_name" jdbcType="VARCHAR"/>
<result property="shop_id" column="shop_id" jdbcType="INTEGER"/>
<result property="state" column="state" jdbcType="TINYINT"/>
<result property="create_time" column="create_time" jdbcType="TIMESTAMP"/>
<result property="call_time" column="call_time" jdbcType="TIMESTAMP"/>
<result property="call_count" column="call_count" jdbcType="INTEGER"/>
<result property="pass_time" column="pass_time" jdbcType="TIMESTAMP"/>
<result property="cancel_time" column="cancel_time" jdbcType="TIMESTAMP"/>
<result property="note" column="note" jdbcType="VARCHAR"/>
<result property="user_id" column="user_id" jdbcType="INTEGER"/>
<result property="open_id" column="open_id" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,call_table_id,phone,
name,shop_name,shop_id,
state,create_time,call_time,
call_count,pass_time,cancel_time,
note,user_id,open_id
</sql>
</mapper>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.ysk.cashier.mybatis.mapper.TbCallTableMapper">
<resultMap id="BaseResultMap" type="cn.ysk.cashier.mybatis.entity.TbCallTable">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="note" column="note" jdbcType="VARCHAR"/>
<result property="wait_time" column="wait_time" jdbcType="INTEGER"/>
<result property="prefix" column="prefix" jdbcType="VARCHAR"/>
<result property="start" column="start" jdbcType="INTEGER"/>
<result property="near_num" column="near_num" jdbcType="INTEGER"/>
<result property="state" column="state" jdbcType="TINYINT"/>
<result property="shop_id" column="shop_id" jdbcType="INTEGER"/>
<result property="qrcode" column="qrcode" jdbcType="VARCHAR"/>
<result property="create_time" column="create_time" jdbcType="TIMESTAMP"/>
<result property="update_time" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,name,note,
wait_time,prefix,start,
near_num,state,shop_id,
qrcode,create_time,update_time
</sql>
</mapper>