lb用户同步的代码结构

This commit is contained in:
2026-05-19 07:29:43 +08:00
parent 8ddab219b7
commit 7c9ee9101e
10 changed files with 673 additions and 16 deletions

View File

@@ -60,6 +60,7 @@ public class MybatisPlusConfig {
ignoreTables.add("industry_tags"); // 行业标签(示例:如认为是公共字典)
ignoreTables.add("menu"); // 菜单表(不需要租户隔离)
ignoreTables.add("ai_prompts"); // AI 提示词全局配置,表无 tenant_id 字段
ignoreTables.add("lb_user"); // LB 用户表,表无 tenant_id 字段
return new TenantLineHandler() {

View File

@@ -0,0 +1,120 @@
package com.rj.controller;
import com.rj.entity.LbUser;
import com.rj.service.ILbUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/lbUser")
@Tag(name = "LB 用户", description = "lb_user 增删改查与分页")
public class LbUserController {
@Autowired
private ILbUserService lbUserService;
@PostMapping("/add")
@Operation(summary = "新增")
public ResponseEntity<Map<String, Object>> add(
@Parameter(description = "实体id 为外部用户 id需调用方指定", required = true)
@RequestBody LbUser entity) {
Map<String, Object> result = lbUserService.add(entity);
Boolean success = (Boolean) result.get("success");
if (success != null && success) {
return ResponseEntity.ok(result);
}
return ResponseEntity.badRequest().body(result);
}
@PutMapping("/update")
@Operation(summary = "编辑")
public ResponseEntity<Map<String, Object>> update(
@Parameter(description = "实体", required = true) @RequestBody LbUser entity) {
Map<String, Object> result = lbUserService.update(entity);
Boolean success = (Boolean) result.get("success");
if (success != null && success) {
return ResponseEntity.ok(result);
}
return ResponseEntity.badRequest().body(result);
}
@DeleteMapping("/delete/{id}")
@Operation(summary = "删除")
public ResponseEntity<Map<String, Object>> delete(
@Parameter(description = "主键(用户 id", required = true) @PathVariable Long id) {
Map<String, Object> result = lbUserService.deleteById(id);
Boolean success = (Boolean) result.get("success");
if (success != null && success) {
return ResponseEntity.ok(result);
}
return ResponseEntity.badRequest().body(result);
}
@GetMapping("/get/{id}")
@Operation(summary = "根据ID查询")
public ResponseEntity<Map<String, Object>> getById(
@Parameter(description = "用户 id", required = true) @PathVariable Long id) {
LbUser data = lbUserService.getById(id);
if (data == null) {
return ResponseEntity.badRequest().body(Map.of("success", false, "message", "记录不存在"));
}
return ResponseEntity.ok(Map.of("success", true, "message", "查询成功", "data", data));
}
@GetMapping("/list")
@Operation(summary = "分页查询")
public ResponseEntity<Map<String, Object>> list(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) Long pid,
@RequestParam(required = false) String username,
@RequestParam(required = false) String nickname,
@RequestParam(required = false) String mobile,
@RequestParam(required = false) String invite,
@RequestParam(required = false) Integer status,
@RequestParam(required = false) Integer isVip,
@RequestParam(required = false) Integer isResell,
@RequestParam(required = false) Integer level,
@RequestParam(required = false) String pname,
@Parameter(description = "注册开始时间格式yyyy-MM-dd HH:mm:ss")
@RequestParam(required = false) String joinTimeStart,
@Parameter(description = "注册结束时间格式yyyy-MM-dd HH:mm:ss")
@RequestParam(required = false) String joinTimeEnd,
@Parameter(description = "更新开始时间格式yyyy-MM-dd HH:mm:ss")
@RequestParam(required = false) String updatedAtStart,
@Parameter(description = "更新结束时间格式yyyy-MM-dd HH:mm:ss")
@RequestParam(required = false) String updatedAtEnd) {
Map<String, Object> result =
lbUserService.pageQuery(
current,
size,
pid,
username,
nickname,
mobile,
invite,
status,
isVip,
isResell,
level,
pname,
joinTimeStart,
joinTimeEnd,
updatedAtStart,
updatedAtEnd);
Boolean success = (Boolean) result.get("success");
if (success != null && success) {
return ResponseEntity.ok(result);
}
if (result.get("message") != null && result.get("message").toString().contains("格式错误")) {
return ResponseEntity.badRequest().body(result);
}
return ResponseEntity.internalServerError().body(result);
}
}

View File

@@ -0,0 +1,167 @@
package com.rj.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("lb_user")
@Schema(description = "LB 用户表")
public class LbUser implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.INPUT)
@Schema(description = "用户ID外部系统主键")
private Long id;
@TableField("pid")
@Schema(description = "上级用户ID")
private Long pid;
@TableField("username")
@Schema(description = "用户名")
private String username;
@TableField("nickname")
@Schema(description = "昵称")
private String nickname;
@TableField("mobile")
@Schema(description = "手机号")
private String mobile;
@TableField("password")
@Schema(description = "密码(加密后)")
private String password;
@TableField("salt")
@Schema(description = "密码盐")
private String salt;
@TableField("sex")
@Schema(description = "性别")
private String sex;
@TableField("avatar")
@Schema(description = "头像路径")
private String avatar;
@TableField("invite")
@Schema(description = "邀请码")
private String invite;
@TableField("level")
@Schema(description = "等级")
private Integer level;
@TableField("birthday")
@Schema(description = "生日")
private LocalDate birthday;
@TableField("money")
@Schema(description = "余额")
private BigDecimal money;
@TableField("coupon")
@Schema(description = "优惠券金额")
private BigDecimal coupon;
@TableField("self_bonus")
@Schema(description = "自购奖金")
private BigDecimal selfBonus;
@TableField("share_bonus")
@Schema(description = "分享奖金")
private BigDecimal shareBonus;
@TableField("score")
@Schema(description = "积分")
private Integer score;
@TableField("last_time")
@Schema(description = "最后登录时间")
private LocalDateTime lastTime;
@TableField("last_ip")
@Schema(description = "最后登录IP")
private String lastIp;
@TableField("join_time")
@Schema(description = "注册时间")
private LocalDateTime joinTime;
@TableField("join_ip")
@Schema(description = "注册IP")
private String joinIp;
@TableField("token")
@Schema(description = "登录令牌")
private String token;
@TableField("created_at")
@Schema(description = "创建时间")
private LocalDateTime createdAt;
@TableField("updated_at")
@Schema(description = "更新时间")
private LocalDateTime updatedAt;
@TableField("status")
@Schema(description = "状态")
private Integer status;
@TableField("viptime")
@Schema(description = "VIP到期时间")
private LocalDateTime viptime;
@TableField("is_vip")
@Schema(description = "是否VIP0否 1是")
private Integer isVip;
@TableField("contract")
@Schema(description = "签约合同文件路径")
private String contract;
@TableField("max_order")
@Schema(description = "最大订单数")
private Integer maxOrder;
@TableField("is_resell")
@Schema(description = "是否可转卖0否 1是")
private Integer isResell;
@TableField("yesterday_sell_count")
@Schema(description = "昨日卖出笔数")
private Integer yesterdaySellCount;
@TableField("today_buy_count")
@Schema(description = "今日买入笔数")
private Integer todayBuyCount;
@TableField("today_buy_total")
@Schema(description = "今日买入总额")
private BigDecimal todayBuyTotal;
@TableField("today_sell_total")
@Schema(description = "今日卖出总额")
private BigDecimal todaySellTotal;
@TableField("poor")
@Schema(description = "贫困标识")
private Integer poor;
@TableField("pname")
@Schema(description = "上级用户昵称")
private String pname;
}

View File

@@ -0,0 +1,9 @@
package com.rj.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.rj.entity.LbUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LbUserMapper extends BaseMapper<LbUser> {
}

View File

@@ -79,9 +79,9 @@ public class LBAdminPullScheduler {
/**
* 未支付/已支付订单同步:工作日每天 11:0015:00应用时区仅在窗口内真正执行。
*/
@Scheduled(
fixedRate = UnPay_MINUTES_MS,
initialDelayString = "${hxr.admin.pull-initial-delay-ms:0}")
// @Scheduled(
// fixedRate = UnPay_MINUTES_MS,
// initialDelayString = "${hxr.admin.pull-initial-delay-ms:0}")
public void pullOrdersForUnPay() {
try {
if (!appConfig.getScheduler().isStart()) {

View File

@@ -169,9 +169,9 @@ public class HxrAdminOrderSelectService {
}
HxrOrderSelectResponse body = opt.get();
log.info("{} order/select ok count={} allMoney={}", label, body.count(), body.allMoney());
for (HxrOrderRow row : body.data()) {
log.info("hxr order row {}", JSON.writeValueAsString(row));
}
// for (HxrOrderRow row : body.data()) {
// log.info("hxr order row {}", JSON.writeValueAsString(row));
// }
}
private static String abbreviate(String s, int maxLen) {

View File

@@ -0,0 +1,33 @@
package com.rj.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.rj.entity.LbUser;
import java.util.Map;
public interface ILbUserService extends IService<LbUser> {
Map<String, Object> add(LbUser entity);
Map<String, Object> update(LbUser entity);
Map<String, Object> deleteById(Long id);
Map<String, Object> pageQuery(
Integer current,
Integer size,
Long pid,
String username,
String nickname,
String mobile,
String invite,
Integer status,
Integer isVip,
Integer isResell,
Integer level,
String pname,
String joinTimeStart,
String joinTimeEnd,
String updatedAtStart,
String updatedAtEnd);
}

View File

@@ -48,7 +48,7 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
private static final int DINGTALK_SELLER_CONFIRM_BATCH_SIZE = 15;
private static final String DINGTALK_SELLER_CONFIRM_PREFIX =
"买家已支付,不要影响 对方寄卖, 请如下卖家确认:";
"提醒,请如下卖家确认,不要耽误 买方寄卖";
private static final long SUM_PLACEHOLDER_USER_ID = 10000L;
@@ -458,16 +458,18 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
return result;
}
List<String> sellerNames =
details.stream()
.map(this::resolveSellerDisplayName)
.distinct()
.limit(DINGTALK_SELLER_CONFIRM_BATCH_SIZE)
.collect(Collectors.toList());
int messageCount = 0;
for (int i = 0; i < details.size(); i += DINGTALK_SELLER_CONFIRM_BATCH_SIZE) {
int end = Math.min(i + DINGTALK_SELLER_CONFIRM_BATCH_SIZE, details.size());
List<LbOrderRow> batch = details.subList(i, end);
String sellerNames =
batch.stream()
.map(this::resolveSellerDisplayName)
.collect(Collectors.joining(","));
dingTalkRobotService.sendText(DINGTALK_SELLER_CONFIRM_PREFIX + sellerNames);
messageCount++;
if (!sellerNames.isEmpty()) {
dingTalkRobotService.sendText(
DINGTALK_SELLER_CONFIRM_PREFIX + String.join(",", sellerNames));
messageCount = 1;
}
result.put("success", true);

View File

@@ -0,0 +1,279 @@
package com.rj.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rj.entity.LbUser;
import com.rj.mapper.LbUserMapper;
import com.rj.service.ILbUserService;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.Map;
@Service
public class LbUserServiceImpl extends ServiceImpl<LbUserMapper, LbUser> implements ILbUserService {
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public Map<String, Object> add(LbUser entity) {
Map<String, Object> result = new HashMap<>();
try {
if (entity.getId() == null) {
result.put("success", false);
result.put("message", "id不能为空");
return result;
}
if (this.getById(entity.getId()) != null) {
result.put("success", false);
result.put("message", "该用户 id 已存在");
return result;
}
applyDefaults(entity);
LocalDateTime now = LocalDateTime.now();
if (entity.getCreatedAt() == null) {
entity.setCreatedAt(now);
}
if (entity.getUpdatedAt() == null) {
entity.setUpdatedAt(now);
}
boolean ok = this.save(entity);
result.put("success", ok);
result.put("message", ok ? "新增成功" : "新增失败");
if (ok) {
result.put("data", this.getById(entity.getId()));
}
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "新增异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> update(LbUser entity) {
Map<String, Object> result = new HashMap<>();
try {
if (entity.getId() == null) {
result.put("success", false);
result.put("message", "id不能为空");
return result;
}
if (this.getById(entity.getId()) == null) {
result.put("success", false);
result.put("message", "记录不存在");
return result;
}
entity.setUpdatedAt(LocalDateTime.now());
boolean ok = this.updateById(entity);
result.put("success", ok);
result.put("message", ok ? "编辑成功" : "编辑失败");
if (ok) {
result.put("data", this.getById(entity.getId()));
}
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "编辑异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> deleteById(Long id) {
Map<String, Object> result = new HashMap<>();
try {
boolean ok = this.removeById(id);
result.put("success", ok);
result.put("message", ok ? "删除成功" : "删除失败");
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "删除异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> pageQuery(
Integer current,
Integer size,
Long pid,
String username,
String nickname,
String mobile,
String invite,
Integer status,
Integer isVip,
Integer isResell,
Integer level,
String pname,
String joinTimeStart,
String joinTimeEnd,
String updatedAtStart,
String updatedAtEnd) {
Map<String, Object> result = new HashMap<>();
try {
if (current == null || current < 1) {
current = 1;
}
if (size == null || size < 1) {
size = 10;
}
LambdaQueryWrapper<LbUser> w = new LambdaQueryWrapper<>();
if (pid != null) {
w.eq(LbUser::getPid, pid);
}
if (username != null && !username.trim().isEmpty()) {
w.like(LbUser::getUsername, username.trim());
}
if (nickname != null && !nickname.trim().isEmpty()) {
w.like(LbUser::getNickname, nickname.trim());
}
if (mobile != null && !mobile.trim().isEmpty()) {
w.like(LbUser::getMobile, mobile.trim());
}
if (invite != null && !invite.trim().isEmpty()) {
w.eq(LbUser::getInvite, invite.trim());
}
if (status != null) {
w.eq(LbUser::getStatus, status);
}
if (isVip != null) {
w.eq(LbUser::getIsVip, isVip);
}
if (isResell != null) {
w.eq(LbUser::getIsResell, isResell);
}
if (level != null) {
w.eq(LbUser::getLevel, level);
}
if (pname != null && !pname.trim().isEmpty()) {
w.like(LbUser::getPname, pname.trim());
}
LocalDateTime joinStart = parseDateTime(joinTimeStart);
if (joinTimeStart != null && !joinTimeStart.trim().isEmpty() && joinStart == null) {
result.put("success", false);
result.put("message", "joinTimeStart 格式错误,请使用 yyyy-MM-dd HH:mm:ss");
return result;
}
LocalDateTime joinEnd = parseDateTime(joinTimeEnd);
if (joinTimeEnd != null && !joinTimeEnd.trim().isEmpty() && joinEnd == null) {
result.put("success", false);
result.put("message", "joinTimeEnd 格式错误,请使用 yyyy-MM-dd HH:mm:ss");
return result;
}
if (joinStart != null) {
w.ge(LbUser::getJoinTime, joinStart);
}
if (joinEnd != null) {
w.le(LbUser::getJoinTime, joinEnd);
}
LocalDateTime updatedStart = parseDateTime(updatedAtStart);
if (updatedAtStart != null && !updatedAtStart.trim().isEmpty() && updatedStart == null) {
result.put("success", false);
result.put("message", "updatedAtStart 格式错误,请使用 yyyy-MM-dd HH:mm:ss");
return result;
}
LocalDateTime updatedEnd = parseDateTime(updatedAtEnd);
if (updatedAtEnd != null && !updatedAtEnd.trim().isEmpty() && updatedEnd == null) {
result.put("success", false);
result.put("message", "updatedAtEnd 格式错误,请使用 yyyy-MM-dd HH:mm:ss");
return result;
}
if (updatedStart != null) {
w.ge(LbUser::getUpdatedAt, updatedStart);
}
if (updatedEnd != null) {
w.le(LbUser::getUpdatedAt, updatedEnd);
}
w.orderByDesc(LbUser::getUpdatedAt).orderByDesc(LbUser::getId);
Page<LbUser> page = this.page(new Page<>(current, size), w);
result.put("success", true);
result.put("message", "查询成功");
result.put("data", page.getRecords());
result.put("total", page.getTotal());
result.put("current", page.getCurrent());
result.put("size", page.getSize());
result.put("pages", page.getPages());
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "查询异常:" + e.getMessage());
return result;
}
}
private static void applyDefaults(LbUser entity) {
if (entity.getLevel() == null) {
entity.setLevel(0);
}
if (entity.getMoney() == null) {
entity.setMoney(BigDecimal.ZERO);
}
if (entity.getCoupon() == null) {
entity.setCoupon(BigDecimal.ZERO);
}
if (entity.getSelfBonus() == null) {
entity.setSelfBonus(BigDecimal.ZERO);
}
if (entity.getShareBonus() == null) {
entity.setShareBonus(BigDecimal.ZERO);
}
if (entity.getScore() == null) {
entity.setScore(0);
}
if (entity.getStatus() == null) {
entity.setStatus(1);
}
if (entity.getIsVip() == null) {
entity.setIsVip(0);
}
if (entity.getMaxOrder() == null) {
entity.setMaxOrder(0);
}
if (entity.getIsResell() == null) {
entity.setIsResell(0);
}
if (entity.getYesterdaySellCount() == null) {
entity.setYesterdaySellCount(0);
}
if (entity.getTodayBuyCount() == null) {
entity.setTodayBuyCount(0);
}
if (entity.getTodayBuyTotal() == null) {
entity.setTodayBuyTotal(BigDecimal.ZERO);
}
if (entity.getTodaySellTotal() == null) {
entity.setTodaySellTotal(BigDecimal.ZERO);
}
if (entity.getPoor() == null) {
entity.setPoor(0);
}
}
private static LocalDateTime parseDateTime(String text) {
if (text == null || text.trim().isEmpty()) {
return null;
}
try {
return LocalDateTime.parse(text.trim(), DATETIME_FMT);
} catch (DateTimeParseException e) {
return null;
}
}
}

46
src/main/sql/lb_user.sql Normal file
View File

@@ -0,0 +1,46 @@
SET NAMES utf8mb4;
CREATE TABLE `lb_user` (
`id` BIGINT NOT NULL COMMENT '用户ID外部系统主键',
`pid` BIGINT DEFAULT NULL COMMENT '上级用户ID',
`username` VARCHAR(64) DEFAULT NULL COMMENT '用户名(常为手机号)',
`nickname` VARCHAR(100) DEFAULT NULL COMMENT '昵称',
`mobile` VARCHAR(32) DEFAULT NULL COMMENT '手机号',
`password` VARCHAR(64) DEFAULT NULL COMMENT '密码(加密后)',
`salt` VARCHAR(32) DEFAULT NULL COMMENT '密码盐',
`sex` VARCHAR(8) DEFAULT NULL COMMENT '性别',
`avatar` VARCHAR(512) DEFAULT NULL COMMENT '头像路径',
`invite` VARCHAR(32) DEFAULT NULL COMMENT '邀请码',
`level` INT NOT NULL DEFAULT 0 COMMENT '等级',
`birthday` DATE DEFAULT NULL COMMENT '生日',
`money` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '余额',
`coupon` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '优惠券金额',
`self_bonus` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '自购奖金',
`share_bonus` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '分享奖金',
`score` INT NOT NULL DEFAULT 0 COMMENT '积分',
`last_time` DATETIME DEFAULT NULL COMMENT '最后登录时间',
`last_ip` VARCHAR(45) DEFAULT NULL COMMENT '最后登录IP',
`join_time` DATETIME DEFAULT NULL COMMENT '注册时间',
`join_ip` VARCHAR(45) DEFAULT NULL COMMENT '注册IP',
`token` VARCHAR(255) DEFAULT NULL COMMENT '登录令牌',
`created_at` DATETIME DEFAULT NULL COMMENT '创建时间',
`updated_at` DATETIME DEFAULT NULL COMMENT '更新时间',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态',
`viptime` DATETIME DEFAULT NULL COMMENT 'VIP到期时间',
`is_vip` TINYINT NOT NULL DEFAULT 0 COMMENT '是否VIP0否 1是',
`contract` VARCHAR(512) DEFAULT NULL COMMENT '签约合同文件路径',
`max_order` INT NOT NULL DEFAULT 0 COMMENT '最大订单数',
`is_resell` TINYINT NOT NULL DEFAULT 0 COMMENT '是否可转卖0否 1是',
`yesterday_sell_count` INT NOT NULL DEFAULT 0 COMMENT '昨日卖出笔数',
`today_buy_count` INT NOT NULL DEFAULT 0 COMMENT '今日买入笔数',
`today_buy_total` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '今日买入总额',
`today_sell_total` DECIMAL(12,3) NOT NULL DEFAULT 0.000 COMMENT '今日卖出总额',
`poor` TINYINT NOT NULL DEFAULT 0 COMMENT '贫困标识/标记',
`pname` VARCHAR(100) DEFAULT NULL COMMENT '上级用户昵称(接口关联字段,可选)',
PRIMARY KEY (`id`),
KEY `idx_pid` (`pid`),
KEY `idx_mobile` (`mobile`),
KEY `idx_username` (`username`),
KEY `idx_join_time` (`join_time`),
KEY `idx_updated_at` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='LB 用户表hxrd 后台 user/select 同步)';