Compare commits

...

5 Commits

Author SHA1 Message Date
dzq 00bb295cb0 feat: 添加订单商品关联格口ID并优化微信授权逻辑
在`shop_order_goods`表中添加`cell_id`字段,用于关联订单商品与柜子格口。同时优化微信授权逻辑,改进token提取与处理方式,并增加日志记录。此外,调整订单服务中柜子格口的验证逻辑,确保订单商品已绑定柜子格口。
2025-04-18 15:55:11 +08:00
dzq ea22f44e41 feat(登录): 新增通过token快速登录功能
新增SsoLoginUserinfo类用于存储登录用户信息,并在WxLoginController中实现tokenLogin接口,支持通过token快速登录。同时优化了ShopController中的微信授权逻辑,支持传递token参数。
2025-04-18 08:20:21 +08:00
dzq 41dc6aee69 feat: 添加格口库存管理功能并优化商品查询
本次提交主要添加了格口库存管理功能,包括在`cabinet_cell`表中新增`stock`字段,并在相关实体类、DTO、控制器和服务中实现库存的配置和查询。同时,优化了商品查询逻辑,支持按柜机ID查询商品,并在商品DTO中添加了`total_stock`字段以显示已分配库存。此外,还在订单相关实体类中添加了`name`字段,用于存储用户姓名。
2025-04-17 16:39:52 +08:00
dzq 774dc07d1a feat(商品管理): 新增商品与柜机格口关联查询功能
在商品管理模块中,新增了查询商品及其关联的柜机格口信息的功能。该功能通过新增的 `SearchGoodsWithCabinetDO` 数据对象、`getGoodsWithCabinetList` 方法以及相应的 SQL 查询实现,支持在客户端按柜机格口显示商品列表。同时,调整了 `ShopController` 中的商品列表获取逻辑,以适配新的查询方式。
2025-04-17 09:18:29 +08:00
dzq 2ec5255615 feat(企业微信): 添加获取管理员用户ID的功能并优化消息发送逻辑
添加`selectAdminUserIds`方法以获取管理员用户ID,并优化`sendNewsMessage`方法中的URL参数,移除调试参数。此外,调整订单商品状态验证逻辑,允许状态为5的商品进行操作。
2025-04-15 16:54:23 +08:00
35 changed files with 419 additions and 41 deletions

View File

@ -73,4 +73,12 @@ public class CabinetCellController extends BaseController {
cabinetCellApplicationService.configureGoodsCells(cellId, goodsId);
return ResponseDTO.ok();
}
@Operation(summary = "配置格口商品库存")
@AccessLog(title = "格口管理", businessType = BusinessTypeEnum.MODIFY)
@PutMapping("/configureGoodsCellsStock/{cellId}/{goodsId}/{stock}")
public ResponseDTO<Void> configureGoodsCellsStock(@PathVariable Long cellId, @PathVariable Long goodsId, @PathVariable Integer stock) {
cabinetCellApplicationService.configureGoodsCellsStock(cellId, goodsId, stock);
return ResponseDTO.ok();
}
}

View File

@ -71,6 +71,11 @@ public class SmartCabinetController extends BaseController {
return ResponseDTO.ok();
}
@Operation(summary = "获取所有柜机数据")
@GetMapping("/allCabinets")
public ResponseDTO<List<SmartCabinetEntity>> allCabinets() {
return ResponseDTO.ok(smartCabinetApplicationService.selectAll());
}
@Operation(summary = "获取所有柜机和格口数据")
@GetMapping("/all")

View File

@ -1,18 +1,28 @@
package com.agileboot.api.controller;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.agileboot.api.response.ShopGoodsResponse;
import com.agileboot.common.constant.WeixinConstants;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.domain.shop.category.CategoryApplicationService;
import com.agileboot.domain.shop.category.db.ShopCategoryEntity;
import com.agileboot.domain.shop.category.dto.ShopCategoryDTO;
import com.agileboot.domain.shop.goods.GoodsApplicationService;
import com.agileboot.domain.shop.goods.db.SearchGoodsWithCabinetDO;
import com.agileboot.domain.shop.goods.db.ShopGoodsEntity;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.util.UriComponentsBuilder;
@ -20,6 +30,7 @@ import org.springframework.web.util.UriComponentsBuilder;
@RestController
@RequestMapping("/api/shop")
@CrossOrigin(origins = "*", allowedHeaders = "*")
@Slf4j
@RequiredArgsConstructor
public class ShopController {
@ -28,7 +39,7 @@ public class ShopController {
@GetMapping("/goods")
public ResponseDTO<ShopGoodsResponse> getShopGoodsInfo() {
// 获取商品列表
/*// 获取商品列表
List<ShopGoodsEntity> goodsList = goodsApplicationService.getGoodsAll();
goodsList.forEach(goods -> {
// 最多只能购买一件
@ -37,25 +48,69 @@ public class ShopController {
}
});
// 获取分类列表
List<ShopCategoryDTO> categoryList = categoryApplicationService.getCategoryAll();
List<ShopCategoryDTO> categoryList = categoryApplicationService.getCategoryAll();*/
// 客户端显示商品列表按柜机格口
List<SearchGoodsWithCabinetDO> goodsWithCabinetList = goodsApplicationService.getGoodsWithCabinetList();
goodsWithCabinetList.forEach(goodsWithCabinet -> {
goodsWithCabinet.setCategoryId(goodsWithCabinet.getCabinetId());
});
return ResponseDTO.ok(new ShopGoodsResponse(goodsList, categoryList, "0"));
List<ShopCategoryDTO> categoryList = goodsWithCabinetList.stream().map(g -> {
ShopCategoryDTO categoryDTO = new ShopCategoryDTO(new ShopCategoryEntity());
categoryDTO.setCategoryId(g.getCategoryId());
categoryDTO.setCategoryName(g.getCabinetName());
return categoryDTO;
}).distinct().collect(Collectors.toList());
return ResponseDTO.ok(new ShopGoodsResponse(goodsWithCabinetList, categoryList, "0"));
}
@GetMapping("/wechatAuth")
public RedirectView wechatAuthRedirect() {
public RedirectView wechatAuthRedirect(HttpServletRequest request) {
/*java.util.StringJoiner joiner = new java.util.StringJoiner("&");
request.getParameterMap().forEach((key, values) -> {
joiner.add(key + "=" + String.join(",", values));
log.info("wechatAuth key: {} value: {}", key, values[0]);
});
log.info("wechatAuth 参数:{}", joiner.toString());*/
// 从请求参数中提取包含token的参数名参数名本身可能包含"token="
String token = request.getParameterMap().keySet().stream()
.filter(key -> key.contains("token="))
.findFirst().orElse("");
log.info("wechatAuth token{}", token);
String state = "";
if (StringUtils.isNotBlank(token)) {
// 清洗token参数值原参数名格式为"token=xxx"
token = token.replace("token=", "");
// 构造带有token标识的state参数用于授权后回传
state = "token_" + token;
} else {
// 默认state参数保持微信要求的标准格式
state = "state";
}
// 构造微信网页授权URL
String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize"
+ "?appid=" + WeixinConstants.appid
+ "&redirect_uri=http%3A%2F%2Fwxshop.ab98.cn%2Fshop"
+ "&response_type=code"
+ "&scope=snsapi_base"
+ "&state=STATE#wechat_redirect";
+ "&state=" + state
+ "#wechat_redirect";
return new RedirectView(authUrl);
}
@GetMapping("/qy/wechatAuth")
public RedirectView qyWechatAuthRedirect() {
public RedirectView qyWechatAuthRedirect(HttpServletRequest request) {
/*java.util.StringJoiner joiner = new java.util.StringJoiner("&");
request.getParameterMap().forEach((key, values) -> {
joiner.add(key + "=" + String.join(",", values));
});
log.info("/qy/wechatAuth 参数:{}", joiner.toString());*/
String authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize"
+ "?appid=" + WeixinConstants.corpid
+ "&redirect_uri=http%3A%2F%2Fwxshop.ab98.cn%2Fshop-api%2Fapi%2Fshop%2FapprovalRedirect"
@ -82,4 +137,9 @@ public class ShopController {
return new RedirectView(builder.build().encode().toUriString());
}
public static void main(String[] args) throws UnsupportedEncodingException {
String res = URLEncoder.encode("token=12312", StandardCharsets.UTF_8.name());
System.out.println(res);
}
}

View File

@ -2,15 +2,19 @@ package com.agileboot.api.controller;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.ab98.api.SsoLoginUserinfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import com.agileboot.domain.ab98.api.Ab98ApiUtil;
import javax.validation.constraints.NotBlank;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/wx/login")
@CrossOrigin(origins = "*", allowedHeaders = "*")
@ -88,7 +92,28 @@ public class WxLoginController {
data.setTel(loginResponse.getOutputData().getTel());
return ResponseDTO.ok(data);
} catch (ApiException e) {
log.error("短信验证失败: {}", e.getMessage());
return ResponseDTO.fail(e);
}
}
@GetMapping("/tokenLogin")
@ApiOperation(value = "通过token登录", notes = "用于快速登录,无需验证码")
public ResponseDTO<Ab98ApiUtil.LoginData> tokenLogin(@RequestParam String token) {
SsoLoginUserinfo loginUserinfo = Ab98ApiUtil.getLoginUserInfoRemote("wxshop", "34164e41f0c6694be6bbbba0dc50c14a", token, "");
Ab98ApiUtil.LoginData data = new Ab98ApiUtil.LoginData();
if (loginUserinfo == null) {
return ResponseDTO.fail(new ApiException(ErrorCode.FAILED, "登录失败,请重新尝试"));
}
data.setFace_img(loginUserinfo.getFace());
data.setSuccess(true);
data.setSex(loginUserinfo.getSex());
data.setName(loginUserinfo.getName());
data.setUserid(String.valueOf(loginUserinfo.getId()));
data.setRegistered(true);
data.setTel(loginUserinfo.getPhone());
return ResponseDTO.ok(data);
}
}

View File

@ -2,6 +2,7 @@ package com.agileboot.api.response;
import com.agileboot.domain.shop.category.dto.ShopCategoryDTO;
import com.agileboot.domain.shop.goods.db.SearchGoodsWithCabinetDO;
import com.agileboot.domain.shop.goods.db.ShopGoodsEntity;
import java.util.List;
import lombok.AllArgsConstructor;
@ -10,7 +11,7 @@ import lombok.Data;
@Data
@AllArgsConstructor
public class ShopGoodsResponse {
private List<ShopGoodsEntity> goodsList;
private List<SearchGoodsWithCabinetDO> goodsList;
private List<ShopCategoryDTO> categoryList;
private String res;
}

View File

@ -1,11 +1,15 @@
package com.agileboot.domain.ab98.api;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
@ -22,9 +26,6 @@ public class Ab98ApiUtil {
* 发送短信验证码短信登录
* @param token 通过getToken获取的临时令牌有效期5分钟
* @param tel 接收验证码的手机号码需符合格式11位数字
* @param nobind "true"表示不绑定手机号固定值
* @param for_login "true"表示用于登录固定值
* @param from 来源渠道固定值"jt"
*/
public static SmsSendResponse sendLoginSms(String token, String tel) {
String url = BASE_URL + "?code=doSendSms";
@ -146,6 +147,38 @@ public class Ab98ApiUtil {
}
}
/**
* 获取登录用户信息
* @param appname
* @param appsecret
* @param token
* @param companyName
* @return
*/
public static SsoLoginUserinfo getLoginUserInfoRemote(String appname,
String appsecret,
String token,
String companyName) {
String sign = DigestUtils.md5Hex(String.format("app=%s&token=%s%s", appname, token, appsecret));
String url = String.format("https://www.ab98.cn/api/getLoginUserInfo?token=%s&app=%s&company_name=%s&sign=%s"
,token
,appname
,""
,sign);
String res = HttpUtil.get(url);
try {
JSONObject o = JSONUtil.parseObj(res);
String state = o.getStr("state");
if (StringUtils.equalsIgnoreCase(state, "ok")) {
return o.get("data", SsoLoginUserinfo.class);
}
} catch (Exception e) {
log.error("获取登录用户信息失败", e);
}
return null;
}
// 基础响应对象
@Data
public static class BaseResponse {

View File

@ -0,0 +1,70 @@
package com.agileboot.domain.ab98.api;
import lombok.Data;
@Data
public class SsoLoginUserinfo {
private Long id;
/**
* 缓存失效时间戳()
*/
private Integer expire;
/**
* 账号状态状态1-正常 2-冻结
*/
private Integer status;
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String sex;
/**
* 手机号码
*/
private String phone;
/**
* 微信昵称
*/
private String nickname;
/**
* 微信openid
*/
private String openId;
/**
* 微信unionId
*/
private String unionId;
/**
* 微信头像
*/
private String headImage;
/**
* 人脸图像
*/
private String face;
/**
* 身份证号码
*/
private String idCardNo;
/**
* 身份证正面
*/
private String idCardFront;
/**
* 身份证背面
*/
private String idCardBack;
/**
* 身份证地址
*/
private String address;
/**
* 原登录账号
*/
private String oldAccount;
}

View File

@ -10,6 +10,8 @@ import com.agileboot.domain.cabinet.cell.dto.CabinetCellDTO;
import com.agileboot.domain.cabinet.cell.model.CabinetCellModel;
import com.agileboot.domain.cabinet.cell.model.CabinetCellModelFactory;
import com.agileboot.domain.cabinet.cell.query.SearchCabinetCellQuery;
import com.agileboot.domain.shop.goods.db.ShopGoodsEntity;
import com.agileboot.domain.shop.goods.db.ShopGoodsService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
@ -24,11 +26,25 @@ import org.springframework.web.bind.annotation.PathVariable;
public class CabinetCellApplicationService {
private final CabinetCellService cabinetCellService;
private final CabinetCellModelFactory cabinetCellModelFactory;
private final ShopGoodsService shopGoodsService;
public PageDTO<CabinetCellDTO> getCabinetCellList(SearchCabinetCellQuery<CabinetCellEntity> query) {
Page<CabinetCellEntity> page = cabinetCellService.getCellList(query);
List<ShopGoodsEntity> goodsList = shopGoodsService.selectAll();
List<CabinetCellDTO> dtoList = page.getRecords().stream()
.map(CabinetCellDTO::new)
.map(cell -> {
CabinetCellDTO dto = new CabinetCellDTO(cell);
ShopGoodsEntity goods = goodsList.stream()
.filter(g -> g.getGoodsId().equals(cell.getGoodsId()))
.findFirst()
.orElse(null);
if (goods != null) {
dto.setGoodsName(goods.getGoodsName());
dto.setPrice(goods.getPrice());
dto.setCoverImg(goods.getCoverImg());
}
return dto;
})
.collect(Collectors.toList());
return new PageDTO<>(dtoList, page.getTotal());
}
@ -67,4 +83,12 @@ public class CabinetCellApplicationService {
model.setUsageStatus(2);
model.updateById();
}
public void configureGoodsCellsStock(Long cellId, Long goodsId, Integer stock) {
CabinetCellModel model = cabinetCellModelFactory.loadById(cellId);
model.setGoodsId(goodsId);
model.setStock(stock);
model.setUsageStatus(2);
model.updateById();
}
}

View File

@ -43,6 +43,10 @@ public class CabinetCellEntity extends BaseEntity<CabinetCellEntity> {
@TableField("pin_no")
private Integer pinNo;
@ApiModelProperty("库存数量")
@TableField("stock")
private Integer stock;
@ApiModelProperty("格口类型1小格 2中格 3大格 4超大格")
@TableField("cell_type")
private Integer cellType;

View File

@ -8,6 +8,8 @@ import com.agileboot.domain.cabinet.cell.db.CabinetCellEntity;
import com.agileboot.domain.system.user.db.SysUserEntity;
import lombok.Data;
import java.math.BigDecimal;
@ExcelSheet(name = "柜机格口信息列表")
@Data
public class CabinetCellDTO {
@ -35,6 +37,9 @@ public class CabinetCellDTO {
@ExcelColumn(name = "针脚序号")
private Integer pinNo;
@ExcelColumn(name = "库存数量")
private Integer stock;
@ExcelColumn(name = "格口类型")
private Integer cellType;
@ -47,6 +52,13 @@ public class CabinetCellDTO {
@ExcelColumn(name = "关联商品ID")
private Long goodsId;
@ExcelColumn(name = "商品名称")
private String goodsName;
@ExcelColumn(name = "商品价格")
private BigDecimal price;
@ExcelColumn(name = "封面图URL")
private String coverImg;
@ExcelColumn(name = "操作人")
private String operator;

View File

@ -31,6 +31,7 @@ public class SearchCabinetCellQuery<T> extends AbstractPageQuery<T> {
.eq(cellType != null, "cell_type", cellType)
.eq(usageStatus != null, "usage_status", usageStatus)
.eq(availableStatus != null, "available_status", availableStatus)
.eq("deleted", false)
.between(startTime != null && endTime != null, "create_time", startTime, endTime);
this.timeRangeColumn = "create_time";

View File

@ -25,6 +25,7 @@ public class SearchSmartCabinetQuery<T> extends AbstractPageQuery<T> {
.like(StrUtil.isNotEmpty(cabinetName), "cabinet_name", cabinetName)
.eq(cabinetType != null, "cabinet_type", cabinetType)
.eq(StrUtil.isNotEmpty(templateNo), "template_no", templateNo)
.eq("deleted", false)
.between(startTime != null && endTime != null, "create_time", startTime, endTime);
this.timeRangeColumn = "create_time";

View File

@ -13,10 +13,14 @@ import org.eclipse.paho.client.mqttv3.*;
@RequiredArgsConstructor
public class MqttService implements MqttCallback {
private static final String SERVER_URL = "tcp://221.7.159.46:1883";
private static final String USERNAME = "lock";
private static final String PASSWORD = "lock@ys#6785$";
private static final String TOPIC_FILTER = "lock/up/S4202414S";
private static final String TOPIC = "lock/down/S4202414S";
// private static final String USERNAME = "lock";
// private static final String PASSWORD = "lock@ys#6785$";
// private static final String TOPIC_FILTER = "lock/up/S4202414S";
// private static final String TOPIC = "lock/down/S4202414S";
private static final String USERNAME = "sell";
private static final String PASSWORD = "sell@ys#6785$";
private static final String TOPIC_FILTER = "lock/up/S5200184S";
private static final String TOPIC = "lock/down/S5200184S";
private MqttClient client;
// 设置自定义消息处理器

View File

@ -29,7 +29,7 @@ public class QywxApiUtil {
* @return 消息发送结果
*/
public static NewsMessageResponse sendNewsMessage(String accessToken, Integer agentId, String toUser, String toparty, String totag, List<NewsArticle> articles) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken + "&debug=" + 1;
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
Map<String, Object> params = new HashMap<>();
params.put("touser", toUser);

View File

@ -31,4 +31,16 @@ public interface QyUserMapper extends BaseMapper<QyUserEntity> {
"AND deleted = 0")
QyUserEntity selectByUserid(@Param("userid") String userid, @Param("corpid") String corpid);
@Select("SELECT DISTINCT qu.userid " +
"FROM qy_user qu " +
"LEFT JOIN sys_user_qy_user suqy ON qu.id = suqy.qy_user_id " +
"LEFT JOIN sys_user su ON suqy.sys_user_id = su.user_id " +
"LEFT JOIN sys_role sr ON su.role_id = sr.role_id " +
"WHERE qu.deleted = 0 " +
"AND suqy.deleted = 0 " +
"AND su.deleted = 0 " +
"AND sr.deleted = 0 " +
"AND sr.role_key = 'admin'")
List<String> selectAdminUserIds();
}

View File

@ -20,4 +20,6 @@ public interface QyUserService extends IService<QyUserEntity> {
List<QyUserEntity> selectAll();
QyUserEntity getUserByUserId(String userid, String corpid);
List<String> selectAdminUserIds();
}

View File

@ -38,4 +38,9 @@ public class QyUserServiceImpl extends ServiceImpl<QyUserMapper, QyUserEntity> i
public QyUserEntity getUserByUserId(String userid, String corpid) {
return baseMapper.selectByUserid(userid, corpid);
}
@Override
public List<String> selectAdminUserIds() {
return baseMapper.selectAdminUserIds();
}
}

View File

@ -9,6 +9,7 @@ import com.agileboot.domain.qywx.api.QywxApiUtil;
import com.agileboot.domain.qywx.api.response.NewsArticle;
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import com.agileboot.domain.qywx.user.db.QyUserService;
import com.agileboot.domain.shop.approval.command.AddReturnApprovalCommand;
import com.agileboot.domain.shop.approval.command.UpdateReturnApprovalCommand;
import com.agileboot.domain.shop.approval.db.ReturnApprovalEntity;
@ -57,6 +58,7 @@ public class ReturnApprovalApplicationService {
private final GoodsModelFactory goodsModelFactory;
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
private final AccessTokenApplicationService accessTokenApplicationService;
private final QyUserService qyUserService;
/**
* 获取退货审批列表
@ -203,8 +205,9 @@ public class ReturnApprovalApplicationService {
.filter(a -> "wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw".equals(a.getCorpid()))
.findFirst().orElse(null);
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, authCorpInfo.getCorpid());
// TODO 获取用户ID
String toUser = "woZ1ZrEgAAV9AEdRt1MGQxSg-KDJrDlA|woZ1ZrEgAAoFQl9vWHMj4PkFoSc8FR8w";
// 获取用户ID
List<String> adminUserIds = qyUserService.selectAdminUserIds();
String toUser = String.join("|", adminUserIds);
String toparty = "";
String totag = "";
List<NewsArticle> articles = new ArrayList<>();

View File

@ -84,6 +84,21 @@ public class ReturnApprovalEntity extends BaseEntity<ReturnApprovalEntity> {
@TableField(exist = false)
private String coverImg;
@ApiModelProperty("手机号码")
@TableField(exist = false)
private String mobile;
@ApiModelProperty("企业微信用户ID或汇邦云用户ID")
@TableField(exist = false)
private String userid;
@ApiModelProperty("用户姓名")
@TableField(exist = false)
private String name;
@ApiModelProperty("是否内部用户0否 1汇邦云用户 2企业微信用户")
@TableField(exist = false)
private Integer isInternal;
@Override
public Serializable pkVal() {

View File

@ -18,9 +18,11 @@ import org.apache.ibatis.annotations.Select;
*/
public interface ReturnApprovalMapper extends BaseMapper<ReturnApprovalEntity> {
@Select("SELECT ra.*, sog.goods_name AS goodsName, sog.cover_img AS coverImg " +
@Select("SELECT ra.*, sog.goods_name AS goodsName, sog.cover_img AS coverImg, " +
"so.mobile, so.userid, so.name, so.is_internal AS isInternal " +
"FROM return_approval ra " +
"LEFT JOIN shop_order_goods sog ON ra.order_goods_id = sog.order_goods_id " +
"LEFT JOIN shop_order so ON ra.order_id = so.order_id AND so.deleted = 0 " +
"LEFT JOIN shop_order_goods sog ON ra.order_goods_id = sog.order_goods_id AND sog.deleted = 0 " +
"${ew.customSqlSegment}")
Page<ReturnApprovalEntity> selectApprovalWithGoodsInfo(Page<ReturnApprovalEntity> page,
@Param(Constants.WRAPPER) Wrapper<ReturnApprovalEntity> queryWrapper);

View File

@ -6,9 +6,11 @@ import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.domain.shop.category.db.ShopCategoryEntity;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ExcelSheet(name = "商品分类列表")
@Data
@EqualsAndHashCode(of = {"categoryId", "categoryName"})
public class ShopCategoryDTO {
public ShopCategoryDTO(ShopCategoryEntity entity) {

View File

@ -5,6 +5,7 @@ import com.agileboot.domain.common.command.BulkOperationCommand;
import com.agileboot.domain.shop.goods.command.AddGoodsCommand;
import com.agileboot.domain.shop.goods.command.UpdateGoodsCommand;
import com.agileboot.domain.shop.goods.db.SearchGoodsDO;
import com.agileboot.domain.shop.goods.db.SearchGoodsWithCabinetDO;
import com.agileboot.domain.shop.goods.db.ShopGoodsEntity;
import com.agileboot.domain.shop.goods.db.ShopGoodsService;
import com.agileboot.domain.shop.goods.dto.ShopGoodsDTO;
@ -59,4 +60,9 @@ public class GoodsApplicationService {
public List<ShopGoodsEntity> getGoodsAll(){
return shopGoodsService.selectAll();
}
public List<SearchGoodsWithCabinetDO> getGoodsWithCabinetList() {
return shopGoodsService.getGoodsWithCabinetList();
}
}

View File

@ -18,4 +18,6 @@ public class SearchGoodsDO extends ShopGoodsEntity {
private String cabinetName;
@TableField("cell_no")
private Integer cellNo;
@TableField("total_stock")
private Integer totalStock;
}

View File

@ -0,0 +1,33 @@
package com.agileboot.domain.shop.goods.db;
import com.agileboot.common.core.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("shop_goods")
public class SearchGoodsWithCabinetDO extends BaseEntity<SearchGoodsWithCabinetDO> {
private Long goodsId;
private String goodsName;
private Long categoryId;
private BigDecimal price;
private Integer stock;
private Integer status;
private String coverImg;
private String goodsDetail;
private String remark;
@TableField("sc.cabinet_id")
private Long cabinetId;
@TableField("sc.cabinet_name")
private String cabinetName;
@TableField("cc.cell_id")
private Long cellId;
}

View File

@ -23,7 +23,7 @@ public interface ShopGoodsMapper extends BaseMapper<ShopGoodsEntity> {
* @param queryWrapper 查询条件
* @return 商品分页列表
*/
@Select("SELECT g.goods_id, g.goods_name, g.category_id, g.price, " +
/* @Select("SELECT g.goods_id, g.goods_name, g.category_id, g.price, " +
"g.stock, g.status, g.cover_img, c.category_name, " +
"sc.cabinet_id, sc.cabinet_name, cc.cell_no " +
"FROM shop_goods g " +
@ -34,6 +34,19 @@ public interface ShopGoodsMapper extends BaseMapper<ShopGoodsEntity> {
Page<SearchGoodsDO> getGoodsList(
Page<SearchGoodsDO> page,
@Param(Constants.WRAPPER) Wrapper<SearchGoodsDO> queryWrapper
); */
@Select("SELECT g.goods_id, g.goods_name, g.category_id, g.price, " +
"g.stock, g.status, g.cover_img, SUM(cc.stock) AS total_stock, " +
"GROUP_CONCAT(DISTINCT cc.cell_no) AS cell_no, " +
"GROUP_CONCAT(DISTINCT sc.cabinet_name) AS cabinet_name " +
"FROM shop_goods g " +
"LEFT JOIN cabinet_cell cc ON g.goods_id = cc.goods_id AND cc.deleted = 0 " +
"LEFT JOIN smart_cabinet sc ON cc.cabinet_id = sc.cabinet_id AND sc.deleted = 0 " +
"${ew.customSqlSegment} " +
"GROUP BY g.goods_id")
Page<SearchGoodsDO> getGoodsList(
Page<SearchGoodsDO> page,
@Param(Constants.WRAPPER) Wrapper<SearchGoodsDO> queryWrapper
);
/**
@ -45,4 +58,15 @@ public interface ShopGoodsMapper extends BaseMapper<ShopGoodsEntity> {
"WHERE deleted = 0 " +
"ORDER BY create_time DESC")
List<ShopGoodsEntity> selectAll();
/**
* 查询商品及其关联的柜机格口信息
* @return 商品列表
*/
@Select("SELECT g.*, sc.cabinet_id, sc.cabinet_name, cc.cell_id " +
"FROM shop_goods g " +
"LEFT JOIN cabinet_cell cc ON g.goods_id = cc.goods_id " +
"LEFT JOIN smart_cabinet sc ON cc.cabinet_id = sc.cabinet_id " +
"WHERE g.deleted = 0 AND sc.deleted = 0 AND cc.deleted = 0 AND cc.goods_id IS NOT NULL ")
List<SearchGoodsWithCabinetDO> getGoodsWithCabinetList();
}

View File

@ -17,4 +17,6 @@ public interface ShopGoodsService extends IService<ShopGoodsEntity> {
Page<SearchGoodsDO> getGoodsList(AbstractPageQuery<SearchGoodsDO> query);
List<ShopGoodsEntity> selectAll();
List<SearchGoodsWithCabinetDO> getGoodsWithCabinetList();
}

View File

@ -29,4 +29,9 @@ public class ShopGoodsServiceImpl extends ServiceImpl<ShopGoodsMapper, ShopGoods
public List<ShopGoodsEntity> selectAll() {
return baseMapper.selectAll();
}
@Override
public List<SearchGoodsWithCabinetDO> getGoodsWithCabinetList() {
return baseMapper.getGoodsWithCabinetList();
}
}

View File

@ -18,7 +18,9 @@ public class ShopGoodsDTO {
public ShopGoodsDTO(ShopGoodsEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
if (this.getTotalStock() == null) {
this.setTotalStock(0);
}
/* ShopCategoryEntity category = CacheCenter.categoryCache.get(entity.getCategoryId() + "");
if (category != null) {
this.categoryName = category.getCategoryName();
@ -77,4 +79,6 @@ public class ShopGoodsDTO {
private String cabinetName;
@ExcelColumn(name = "格口号")
private Integer cellNo;
@ExcelColumn(name = "已分配库存")
private Integer totalStock;
}

View File

@ -23,7 +23,7 @@ public class SearchShopGoodsQuery<T> extends AbstractPageQuery<T> {
queryWrapper
.like(StrUtil.isNotEmpty(goodsName), "g.goods_name", goodsName)
.eq(categoryId != null, "g.category_id", categoryId)
.eq(categoryId != null, "cc.cabinet_id", categoryId)
.eq(status != null, "g.status", status)
.ge(minPrice != null, "g.price", minPrice)
.le(maxPrice != null, "g.price", maxPrice)

View File

@ -72,34 +72,28 @@ public class OrderApplicationService {
if (orderModel.getStatus() != 2) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单状态不允许操作");
}
Date payTime = orderModel.getPayTime();
if (payTime == null)
payTime = orderModel.getCreateTime();
// 验证payTime是否在24小时内
if (DateUtil.offsetHour(payTime, 24).before(new Date())) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单支付时间超过24小时不允许操作");
}
// 获取并验证订单商品信息
ShopOrderGoodsEntity goodsEntity = orderGoodsService.getById(orderGoodsId);
if (null == goodsEntity || !orderId.equals(goodsEntity.getOrderId())) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单商品不存在");
}
if (goodsEntity.getStatus() != 1) {
if (goodsEntity.getStatus() != 1 && goodsEntity.getStatus()!= 5) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单商品状态不允许操作");
}
List<CabinetCellEntity> cabinetCellEntityList = cabinetCellService.selectByGoodsId(goodsEntity.getGoodsId());
if (null == cabinetCellEntityList || cabinetCellEntityList.isEmpty()) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "商品未绑定柜子");
if (goodsEntity.getCellId() == null) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单商品未绑定柜子");
}
CabinetCellEntity cabinetCellEntity = cabinetCellEntityList.get(0);
// 获取柜子单元和智能柜信息
CabinetCellEntity cabinetCellEntity = cabinetCellService.getById(goodsEntity.getCellId());
SmartCabinetEntity smartCabinet = smartCabinetService.getById(cabinetCellEntity.getCabinetId());
if (null == smartCabinet) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "柜子不存在");
}
// 发送指令
// 构造MQTT开柜指令
// 指令格式8A + 锁控编号(2位HEX) + 引脚号(2位HEX) + 操作码11
String mqttDate = "8A";
mqttDate += String.format("%02X", smartCabinet.getLockControlNo());
mqttDate += String.format("%02X", cabinetCellEntity.getPinNo());
@ -131,6 +125,7 @@ public class OrderApplicationService {
orderModel.setMobile(command.getMobile());
orderModel.setIsInternal(command.getIsInternal());
orderModel.setUserid(command.getQyUserid());
orderModel.setName(command.getName());
orderModel.insert();
processOrderGoods(orderModel, goodsList);

View File

@ -31,6 +31,9 @@ public class SubmitOrderCommand {
@ApiModelProperty("企业微信用户ID或汇邦云用户ID")
private String qyUserid;
@ApiModelProperty("用户姓名")
private String name;
@ApiModelProperty("是否内部订单 0否 1汇邦云用户 2企业微信用户")
private Integer isInternal;
}

View File

@ -53,6 +53,10 @@ public class ShopOrderEntity extends BaseEntity<ShopOrderEntity> {
@TableField("userid")
private String userid;
@ApiModelProperty("用户姓名")
@TableField("name")
private String name;
@ApiModelProperty("是否内部用户0否 1汇邦云用户 2企业微信用户")
@TableField("is_internal")
private Integer isInternal;

View File

@ -40,6 +40,10 @@ public class ShopOrderGoodsEntity extends BaseEntity<ShopOrderGoodsEntity> {
@TableField("goods_id")
private Long goodsId;
@ApiModelProperty("关联格口ID")
@TableField("cell_id")
private Long cellId;
@ApiModelProperty("购买数量")
@TableField("quantity")
private Integer quantity;

View File

@ -25,6 +25,7 @@ CREATE TABLE `cabinet_cell` (
`goods_id` BIGINT DEFAULT NULL COMMENT '关联商品ID',
`cell_no` INT NOT NULL COMMENT '格口号',
`pin_no` INT NOT NULL COMMENT '针脚序号',
`stock` INT NOT NULL DEFAULT 0 COMMENT '库存数量',
`cell_type` TINYINT NOT NULL DEFAULT 1 COMMENT '格口类型1小格 2中格 3大格 4超大格',
`usage_status` TINYINT NOT NULL DEFAULT 1 COMMENT '使用状态1空闲 2已占用',
`available_status` TINYINT NOT NULL DEFAULT 1 COMMENT '可用状态1正常 2故障',

View File

@ -66,3 +66,9 @@ ADD COLUMN `is_internal` TINYINT(1) DEFAULT 0 COMMENT '是否内部用户0否
ALTER TABLE `shop_order`
ADD COLUMN `userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '企业微信用户ID或汇邦云用户ID' AFTER `mobile`;
ALTER TABLE `shop_order`
ADD COLUMN `name` varchar(30) DEFAULT NULL COMMENT '成员名称' AFTER `mobile`;
ALTER TABLE `shop_order_goods`
ADD COLUMN `cell_id` BIGINT NULL COMMENT '格口ID' AFTER `goods_id`;