用户 / 订单 / 商品 管理

This commit is contained in:
zk
2026-07-10 14:36:44 +08:00
parent 45b2c3073f
commit 20ab909329
14 changed files with 646 additions and 0 deletions
@@ -0,0 +1,36 @@
package org.jiayunet.admin.controller;
import lombok.AllArgsConstructor;
import org.jiayunet.admin.pojo.dto.order.OrderListDto;
import org.jiayunet.admin.pojo.dto.order.OrderStatsDto;
import org.jiayunet.admin.pojo.param.order.OrderPageParam;
import org.jiayunet.admin.service.AdminOrderService;
import org.jiayunet.pojo.PageResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 管理后台-订单管理控制类
*
* @author zk
*/
@RestController
@RequestMapping("/order")
@AllArgsConstructor
@Validated
public class AdminOrderController {
private AdminOrderService adminOrderService;
/** 订单分页列表 */
@PostMapping("/list")
public PageResult<OrderListDto> list(@Validated @RequestBody OrderPageParam param) {
return adminOrderService.pageList(param);
}
/** 订单统计 */
@GetMapping("/stats")
public OrderStatsDto stats() {
return adminOrderService.stats();
}
}
@@ -0,0 +1,48 @@
package org.jiayunet.admin.controller;
import lombok.AllArgsConstructor;
import org.jiayunet.admin.pojo.param.product.ProductPageParam;
import org.jiayunet.admin.pojo.param.product.ProductSaveParam;
import org.jiayunet.admin.service.AdminProductService;
import org.jiayunet.pojo.PageResult;
import org.jiayunet.pojo.po.MemberProduct;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 管理后台-商品管理控制类
*
* @author zk
*/
@RestController
@RequestMapping("/product")
@AllArgsConstructor
@Validated
public class AdminProductController {
private AdminProductService adminProductService;
/** 商品分页列表 */
@PostMapping("/list")
public PageResult<MemberProduct> list(@Validated @RequestBody ProductPageParam param) {
return adminProductService.pageList(param);
}
/** 新增/编辑商品 */
@PostMapping("/save")
public void save(@Validated @RequestBody ProductSaveParam param) {
adminProductService.save(param);
}
/** 上架/下架 */
@PostMapping("/updateStatus")
public void updateStatus(@RequestParam Long id, @RequestParam Integer status) {
adminProductService.updateStatus(id, status);
}
/** 删除商品 */
@PostMapping("/delete")
public void delete(@RequestParam Long id) {
adminProductService.delete(id);
}
}
@@ -0,0 +1,42 @@
package org.jiayunet.admin.controller;
import lombok.AllArgsConstructor;
import org.jiayunet.admin.pojo.dto.user.UserListDto;
import org.jiayunet.admin.pojo.dto.user.UserStatsDto;
import org.jiayunet.admin.pojo.param.user.UserPageParam;
import org.jiayunet.admin.service.AdminUserService;
import org.jiayunet.pojo.PageResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 管理后台-用户管理控制类
*
* @author zk
*/
@RestController
@RequestMapping("/user")
@AllArgsConstructor
@Validated
public class AdminUserController {
private AdminUserService adminUserService;
/** 用户分页列表 */
@PostMapping("/list")
public PageResult<UserListDto> list(@Validated @RequestBody UserPageParam param) {
return adminUserService.pageList(param);
}
/** 用户统计(总数/今日/本周/本月) */
@GetMapping("/stats")
public UserStatsDto stats() {
return adminUserService.stats();
}
/** 禁用/启用用户 */
@PostMapping("/updateStatus")
public void updateStatus(@RequestParam Long userId, @RequestParam Integer status) {
adminUserService.updateStatus(userId, status);
}
}
@@ -0,0 +1,35 @@
package org.jiayunet.admin.pojo.dto.order;
import lombok.Data;
import java.time.Instant;
/**
* 订单列表出参
*
* @author zk
*/
@Data
public class OrderListDto {
/** 订单ID */
private Long id;
/** 订单编号 */
private String orderNo;
/** 用户昵称 */
private String userNick;
/** 用户手机号 */
private String mobileNumber;
/** 商品名称 */
private String productName;
/** 实付金额(分) */
private Integer payAmount;
/** 支付渠道 1=微信 2=支付宝 */
private Integer payChannel;
/** 订单状态 0=待支付 1=已支付 2=已退款 3=已关闭 */
private Integer status;
/** 下单时间 */
private Instant createTime;
/** 支付时间 */
private Instant payTime;
}
@@ -0,0 +1,29 @@
package org.jiayunet.admin.pojo.dto.order;
import lombok.Data;
/**
* 订单统计出参
*
* @author zk
*/
@Data
public class OrderStatsDto {
/** 总订单数 */
private Long totalCount;
/** 已支付订单数 */
private Long paidCount;
/** 今日已支付 */
private Long todayPaid;
/** 本周已支付 */
private Long weekPaid;
/** 本月已支付 */
private Long monthPaid;
/** 今日收入(分) */
private Long todayIncome;
/** 本周收入(分) */
private Long weekIncome;
/** 本月收入(分) */
private Long monthIncome;
}
@@ -0,0 +1,29 @@
package org.jiayunet.admin.pojo.dto.user;
import lombok.Data;
import java.time.Instant;
/**
* 用户列表出参
*
* @author zk
*/
@Data
public class UserListDto {
/** 用户ID */
private Long id;
/** 昵称 */
private String nick;
/** 真实姓名 */
private String realName;
/** 手机号 */
private String mobileNumber;
/** 状态 0=正常 1=禁用 */
private Integer status;
/** 简历数量 */
private Integer resumeCount;
/** 注册时间 */
private Instant createTime;
}
@@ -0,0 +1,21 @@
package org.jiayunet.admin.pojo.dto.user;
import lombok.Data;
/**
* 用户统计出参
*
* @author zk
*/
@Data
public class UserStatsDto {
/** 用户总数 */
private Long total;
/** 今日新增 */
private Long today;
/** 本周新增 */
private Long thisWeek;
/** 本月新增 */
private Long thisMonth;
}
@@ -0,0 +1,28 @@
package org.jiayunet.admin.pojo.param.order;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jiayunet.pojo.PageParam;
import java.time.Instant;
/**
* 订单分页查询入参
*
* @author zk
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class OrderPageParam extends PageParam {
/** 订单状态 0=待支付 1=已支付 2=已退款 3=已关闭 */
private Integer status;
/** 支付渠道 1=微信 2=支付宝 */
private Integer payChannel;
/** 用户手机号(模糊) */
private String mobileNumber;
/** 下单时间-起 */
private Instant startTime;
/** 下单时间-止 */
private Instant endTime;
}
@@ -0,0 +1,18 @@
package org.jiayunet.admin.pojo.param.product;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jiayunet.pojo.PageParam;
/**
* 商品分页查询入参
*
* @author zk
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ProductPageParam extends PageParam {
/** 状态筛选 0=下架 1=上架 */
private Integer status;
}
@@ -0,0 +1,51 @@
package org.jiayunet.admin.pojo.param.product;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 商品新增/编辑入参
*
* @author zk
*/
@Data
public class ProductSaveParam {
/** 商品ID(编辑时必传,新增时不传) */
private Long id;
@NotBlank(message = "商品名称不能为空")
private String productName;
/** 标签,如"限时优惠" */
private String tag;
/** 主推标识 0=否 1=是 */
private Integer isFeatured;
/** 购买按钮文字 */
private String buyButtonText;
@NotNull(message = "价格不能为空")
@Min(value = 1, message = "价格必须大于0")
private Integer price;
/** 划线价(分) */
private Integer originalPrice;
/** 折算月价(分) */
private Integer monthlyPrice;
@NotNull(message = "有效天数不能为空")
@Min(value = 1, message = "有效天数必须大于0")
private Integer durationDays;
/** 排序 */
private Integer sortOrder;
/** 状态 0=下架 1=上架 */
private Integer status;
}
@@ -0,0 +1,21 @@
package org.jiayunet.admin.pojo.param.user;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jiayunet.pojo.PageParam;
/**
* 用户分页查询入参
*
* @author zk
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class UserPageParam extends PageParam {
/** 手机号(模糊搜索) */
private String mobileNumber;
/** 状态筛选 0=正常 1=禁用 */
private Integer status;
}
@@ -0,0 +1,119 @@
package org.jiayunet.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jiayunet.admin.pojo.dto.order.OrderListDto;
import org.jiayunet.admin.pojo.dto.order.OrderStatsDto;
import org.jiayunet.admin.pojo.param.order.OrderPageParam;
import org.jiayunet.mapper.MemberOrderMapper;
import org.jiayunet.mapper.MemberProductMapper;
import org.jiayunet.mapper.UserMapper;
import org.jiayunet.pojo.PageResult;
import org.jiayunet.pojo.po.MemberOrder;
import org.jiayunet.pojo.po.MemberProduct;
import org.jiayunet.pojo.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 管理后台-订单管理服务
* <p>依赖:MemberOrderMapper、UserMapper、MemberProductMapper</p>
* <p>使用表:bg_member_order(订单列表/统计)、bg_user(关联用户信息)、bg_member_product(关联商品名)</p>
*
* @author zk
*/
@Service
@Slf4j
public class AdminOrderService {
@Autowired
private MemberOrderMapper memberOrderMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private MemberProductMapper memberProductMapper;
/**
* 订单分页列表
* <p>1. 如果按手机号筛选,先查用户ID集合 2. 分页查订单 3. 批量关联用户和商品信息 4. 组装DTO</p>
*/
public PageResult<OrderListDto> pageList(OrderPageParam param) {
// 如果传了手机号,先查对应用户ID
Set<Long> userIdFilter = null;
if (StringUtils.hasText(param.getMobileNumber())) {
List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>().like(User::getMobileNumber, param.getMobileNumber()).select(User::getId));
userIdFilter = users.stream().map(User::getId).collect(Collectors.toSet());
if (userIdFilter.isEmpty()) return new PageResult<>(param.getPageNum().longValue(), param.getPageSize().longValue(), 0L, List.of());
}
LambdaQueryWrapper<MemberOrder> wrapper = new LambdaQueryWrapper<MemberOrder>()
.eq(param.getStatus() != null, MemberOrder::getStatus, param.getStatus())
.eq(param.getPayChannel() != null, MemberOrder::getPayChannel, param.getPayChannel())
.ge(param.getStartTime() != null, MemberOrder::getCreateTime, param.getStartTime())
.le(param.getEndTime() != null, MemberOrder::getCreateTime, param.getEndTime())
.in(userIdFilter != null, MemberOrder::getUserId, userIdFilter)
.orderByDesc(MemberOrder::getCreateTime);
Page<MemberOrder> page = memberOrderMapper.selectPage(param.toPage(), wrapper);
if (page.getRecords().isEmpty()) return PageResult.from((Page<OrderListDto>) page.convert(o -> new OrderListDto()));
// 批量查用户和商品
Set<Long> userIds = page.getRecords().stream().map(MemberOrder::getUserId).collect(Collectors.toSet());
Set<Long> productIds = page.getRecords().stream().map(MemberOrder::getProductId).collect(Collectors.toSet());
Map<Long, User> userMap = userMapper.selectBatchIds(userIds).stream().collect(Collectors.toMap(User::getId, u -> u));
Map<Long, MemberProduct> productMap = memberProductMapper.selectBatchIds(productIds).stream().collect(Collectors.toMap(MemberProduct::getId, p -> p));
Page<OrderListDto> dtoPage = (Page<OrderListDto>) page.convert(order -> {
OrderListDto dto = new OrderListDto();
dto.setId(order.getId());
dto.setOrderNo(order.getOrderNo());
dto.setPayAmount(order.getPayAmount());
dto.setPayChannel(order.getPayChannel());
dto.setStatus(order.getStatus());
dto.setCreateTime(order.getCreateTime());
dto.setPayTime(order.getPayTime());
User user = userMap.get(order.getUserId());
if (user != null) { dto.setUserNick(user.getNick()); dto.setMobileNumber(user.getMobileNumber()); }
MemberProduct product = productMap.get(order.getProductId());
if (product != null) { dto.setProductName(product.getProductName()); }
return dto;
});
return PageResult.from(dtoPage);
}
/**
* 订单统计:总数/已支付数/今日本周本月已支付数和收入
*/
public OrderStatsDto stats() {
OrderStatsDto dto = new OrderStatsDto();
ZoneId zone = ZoneId.of("Asia/Shanghai");
LocalDate now = LocalDate.now(zone);
Instant todayStart = now.atStartOfDay(zone).toInstant();
Instant weekStart = now.with(DayOfWeek.MONDAY).atStartOfDay(zone).toInstant();
Instant monthStart = now.withDayOfMonth(1).atStartOfDay(zone).toInstant();
dto.setTotalCount(memberOrderMapper.selectCount(new LambdaQueryWrapper<MemberOrder>()));
dto.setPaidCount(memberOrderMapper.selectCount(new LambdaQueryWrapper<MemberOrder>().eq(MemberOrder::getStatus, 1)));
// 今日/本周/本月已支付订单
List<MemberOrder> todayOrders = memberOrderMapper.selectList(new LambdaQueryWrapper<MemberOrder>().eq(MemberOrder::getStatus, 1).ge(MemberOrder::getPayTime, todayStart));
List<MemberOrder> weekOrders = memberOrderMapper.selectList(new LambdaQueryWrapper<MemberOrder>().eq(MemberOrder::getStatus, 1).ge(MemberOrder::getPayTime, weekStart));
List<MemberOrder> monthOrders = memberOrderMapper.selectList(new LambdaQueryWrapper<MemberOrder>().eq(MemberOrder::getStatus, 1).ge(MemberOrder::getPayTime, monthStart));
dto.setTodayPaid((long) todayOrders.size());
dto.setWeekPaid((long) weekOrders.size());
dto.setMonthPaid((long) monthOrders.size());
dto.setTodayIncome(todayOrders.stream().mapToLong(o -> o.getPayAmount() == null ? 0 : o.getPayAmount()).sum());
dto.setWeekIncome(weekOrders.stream().mapToLong(o -> o.getPayAmount() == null ? 0 : o.getPayAmount()).sum());
dto.setMonthIncome(monthOrders.stream().mapToLong(o -> o.getPayAmount() == null ? 0 : o.getPayAmount()).sum());
return dto;
}
}
@@ -0,0 +1,67 @@
package org.jiayunet.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jiayunet.admin.pojo.param.product.ProductPageParam;
import org.jiayunet.admin.pojo.param.product.ProductSaveParam;
import org.jiayunet.mapper.MemberProductMapper;
import org.jiayunet.pojo.PageResult;
import org.jiayunet.pojo.po.MemberProduct;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
/**
* 管理后台-商品管理服务
* <p>依赖:MemberProductMapper</p>
* <p>使用表:bg_member_productCRUD</p>
*
* @author zk
*/
@Service
@Slf4j
public class AdminProductService {
@Autowired
private MemberProductMapper memberProductMapper;
/** 商品分页列表 */
public PageResult<MemberProduct> pageList(ProductPageParam param) {
LambdaQueryWrapper<MemberProduct> wrapper = new LambdaQueryWrapper<MemberProduct>()
.eq(param.getStatus() != null, MemberProduct::getStatus, param.getStatus())
.orderByAsc(MemberProduct::getSortOrder);
Page<MemberProduct> page = memberProductMapper.selectPage(param.toPage(), wrapper);
return PageResult.from(page);
}
/** 新增/编辑商品 */
public void save(ProductSaveParam param) {
MemberProduct product = new MemberProduct();
BeanUtils.copyProperties(param, product);
if (param.getId() == null) {
if (product.getStatus() == null) product.setStatus(0);
if (product.getSortOrder() == null) product.setSortOrder(0);
if (product.getIsFeatured() == null) product.setIsFeatured(0);
memberProductMapper.insert(product);
} else {
product.setId(param.getId());
memberProductMapper.updateById(product);
}
}
/** 上架/下架商品 */
public void updateStatus(Long id, Integer status) {
Assert.notNull(id, "商品ID不能为空");
Assert.isTrue(status == 0 || status == 1, "状态值非法");
memberProductMapper.update(null, new LambdaUpdateWrapper<MemberProduct>().eq(MemberProduct::getId, id).set(MemberProduct::getStatus, status));
}
/** 删除商品(逻辑删除) */
public void delete(Long id) {
Assert.notNull(id, "商品ID不能为空");
memberProductMapper.deleteById(id);
}
}
@@ -0,0 +1,102 @@
package org.jiayunet.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jiayunet.admin.pojo.dto.user.UserListDto;
import org.jiayunet.admin.pojo.dto.user.UserStatsDto;
import org.jiayunet.admin.pojo.param.user.UserPageParam;
import org.jiayunet.mapper.UserMapper;
import org.jiayunet.mapper.UserResumeMapper;
import org.jiayunet.pojo.PageResult;
import org.jiayunet.pojo.po.User;
import org.jiayunet.pojo.po.UserResume;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.time.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 管理后台-用户管理服务
* <p>依赖:UserMapper(用户查询/更新)、UserResumeMapper(简历数量统计)</p>
* <p>使用表:bg_user(用户列表/统计/禁用)、bg_user_resume(简历数量)</p>
*
* @author zk
*/
@Service
@Slf4j
public class AdminUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserResumeMapper userResumeMapper;
/**
* 用户分页列表
* <p>1. 分页查询用户 2. 批量查询每个用户的简历数量 3. 组装返回</p>
*/
public PageResult<UserListDto> pageList(UserPageParam param) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.like(StringUtils.hasText(param.getMobileNumber()), User::getMobileNumber, param.getMobileNumber())
.like(StringUtils.hasText(param.getKeyword()), User::getNick, param.getKeyword())
.eq(param.getStatus() != null, User::getStatus, param.getStatus())
.orderByDesc(User::getCreateTime);
Page<User> page = userMapper.selectPage(param.toPage(), wrapper);
if (page.getRecords().isEmpty()) return PageResult.from((Page<UserListDto>) page.convert(u -> new UserListDto()));
// 批量查询简历数量
List<Long> userIds = page.getRecords().stream().map(User::getId).collect(Collectors.toList());
List<UserResume> resumes = userResumeMapper.selectList(new LambdaQueryWrapper<UserResume>().in(UserResume::getUserId, userIds).select(UserResume::getUserId));
Map<Long, Long> resumeCountMap = resumes.stream().collect(Collectors.groupingBy(UserResume::getUserId, Collectors.counting()));
// 组装DTO
Page<UserListDto> dtoPage = (Page<UserListDto>) page.convert(user -> {
UserListDto dto = new UserListDto();
dto.setId(user.getId());
dto.setNick(user.getNick());
dto.setRealName(user.getRealName());
dto.setMobileNumber(user.getMobileNumber());
dto.setStatus(user.getStatus());
dto.setResumeCount(resumeCountMap.getOrDefault(user.getId(), 0L).intValue());
dto.setCreateTime(user.getCreateTime());
return dto;
});
return PageResult.from(dtoPage);
}
/**
* 用户统计:总数 / 今日 / 本周 / 本月 新增
*/
public UserStatsDto stats() {
UserStatsDto dto = new UserStatsDto();
ZoneId zone = ZoneId.of("Asia/Shanghai");
LocalDate now = LocalDate.now(zone);
Instant todayStart = now.atStartOfDay(zone).toInstant();
Instant weekStart = now.with(DayOfWeek.MONDAY).atStartOfDay(zone).toInstant();
Instant monthStart = now.withDayOfMonth(1).atStartOfDay(zone).toInstant();
dto.setTotal(userMapper.selectCount(new LambdaQueryWrapper<User>()));
dto.setToday(userMapper.selectCount(new LambdaQueryWrapper<User>().ge(User::getCreateTime, todayStart)));
dto.setThisWeek(userMapper.selectCount(new LambdaQueryWrapper<User>().ge(User::getCreateTime, weekStart)));
dto.setThisMonth(userMapper.selectCount(new LambdaQueryWrapper<User>().ge(User::getCreateTime, monthStart)));
return dto;
}
/**
* 禁用/启用用户
*/
public void updateStatus(Long userId, Integer status) {
Assert.notNull(userId, "用户ID不能为空");
Assert.isTrue(status == 0 || status == 1, "状态值非法");
userMapper.update(null, new LambdaUpdateWrapper<User>().eq(User::getId, userId).set(User::getStatus, status));
}
}