From 7c9ee9101ea931c2d1b7f5b07c6e45a1ec9aac1b Mon Sep 17 00:00:00 2001 From: cst61 Date: Tue, 19 May 2026 07:29:43 +0800 Subject: [PATCH] =?UTF-8?q?lb=E7=94=A8=E6=88=B7=E5=90=8C=E6=AD=A5=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/rj/config/MybatisPlusConfig.java | 1 + .../com/rj/controller/LbUserController.java | 120 ++++++++ src/main/java/com/rj/entity/LbUser.java | 167 +++++++++++ src/main/java/com/rj/mapper/LbUserMapper.java | 9 + .../rj/scheduler/LBAdminPullScheduler.java | 6 +- .../service/HxrAdminOrderSelectService.java | 6 +- .../java/com/rj/service/ILbUserService.java | 33 +++ .../service/impl/LbOrderRowServiceImpl.java | 22 +- .../rj/service/impl/LbUserServiceImpl.java | 279 ++++++++++++++++++ src/main/sql/lb_user.sql | 46 +++ 10 files changed, 673 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/rj/controller/LbUserController.java create mode 100644 src/main/java/com/rj/entity/LbUser.java create mode 100644 src/main/java/com/rj/mapper/LbUserMapper.java create mode 100644 src/main/java/com/rj/service/ILbUserService.java create mode 100644 src/main/java/com/rj/service/impl/LbUserServiceImpl.java create mode 100644 src/main/sql/lb_user.sql diff --git a/src/main/java/com/rj/config/MybatisPlusConfig.java b/src/main/java/com/rj/config/MybatisPlusConfig.java index e5d313f..4b24fd2 100644 --- a/src/main/java/com/rj/config/MybatisPlusConfig.java +++ b/src/main/java/com/rj/config/MybatisPlusConfig.java @@ -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() { diff --git a/src/main/java/com/rj/controller/LbUserController.java b/src/main/java/com/rj/controller/LbUserController.java new file mode 100644 index 0000000..2041618 --- /dev/null +++ b/src/main/java/com/rj/controller/LbUserController.java @@ -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> add( + @Parameter(description = "实体(id 为外部用户 id,需调用方指定)", required = true) + @RequestBody LbUser entity) { + Map 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> update( + @Parameter(description = "实体", required = true) @RequestBody LbUser entity) { + Map 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> delete( + @Parameter(description = "主键(用户 id)", required = true) @PathVariable Long id) { + Map 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> 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> 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 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); + } +} diff --git a/src/main/java/com/rj/entity/LbUser.java b/src/main/java/com/rj/entity/LbUser.java new file mode 100644 index 0000000..24db2c1 --- /dev/null +++ b/src/main/java/com/rj/entity/LbUser.java @@ -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 = "是否VIP:0否 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; +} diff --git a/src/main/java/com/rj/mapper/LbUserMapper.java b/src/main/java/com/rj/mapper/LbUserMapper.java new file mode 100644 index 0000000..8fc3e73 --- /dev/null +++ b/src/main/java/com/rj/mapper/LbUserMapper.java @@ -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 { +} diff --git a/src/main/java/com/rj/scheduler/LBAdminPullScheduler.java b/src/main/java/com/rj/scheduler/LBAdminPullScheduler.java index 74a25b7..1d04faa 100644 --- a/src/main/java/com/rj/scheduler/LBAdminPullScheduler.java +++ b/src/main/java/com/rj/scheduler/LBAdminPullScheduler.java @@ -79,9 +79,9 @@ public class LBAdminPullScheduler { /** * 未支付/已支付订单同步:工作日每天 11:00~15: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()) { diff --git a/src/main/java/com/rj/service/HxrAdminOrderSelectService.java b/src/main/java/com/rj/service/HxrAdminOrderSelectService.java index 7563cc6..4c91f82 100644 --- a/src/main/java/com/rj/service/HxrAdminOrderSelectService.java +++ b/src/main/java/com/rj/service/HxrAdminOrderSelectService.java @@ -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) { diff --git a/src/main/java/com/rj/service/ILbUserService.java b/src/main/java/com/rj/service/ILbUserService.java new file mode 100644 index 0000000..2b11d46 --- /dev/null +++ b/src/main/java/com/rj/service/ILbUserService.java @@ -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 { + + Map add(LbUser entity); + + Map update(LbUser entity); + + Map deleteById(Long id); + + Map 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); +} diff --git a/src/main/java/com/rj/service/impl/LbOrderRowServiceImpl.java b/src/main/java/com/rj/service/impl/LbOrderRowServiceImpl.java index ffee7c3..fe335ff 100644 --- a/src/main/java/com/rj/service/impl/LbOrderRowServiceImpl.java +++ b/src/main/java/com/rj/service/impl/LbOrderRowServiceImpl.java @@ -48,7 +48,7 @@ public class LbOrderRowServiceImpl extends ServiceImpl 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 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); diff --git a/src/main/java/com/rj/service/impl/LbUserServiceImpl.java b/src/main/java/com/rj/service/impl/LbUserServiceImpl.java new file mode 100644 index 0000000..5ab2a75 --- /dev/null +++ b/src/main/java/com/rj/service/impl/LbUserServiceImpl.java @@ -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 implements ILbUserService { + + private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Override + public Map add(LbUser entity) { + Map 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 update(LbUser entity) { + Map 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 deleteById(Long id) { + Map 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 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 result = new HashMap<>(); + try { + if (current == null || current < 1) { + current = 1; + } + if (size == null || size < 1) { + size = 10; + } + + LambdaQueryWrapper 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 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; + } + } +} diff --git a/src/main/sql/lb_user.sql b/src/main/sql/lb_user.sql new file mode 100644 index 0000000..c7db9b4 --- /dev/null +++ b/src/main/sql/lb_user.sql @@ -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 '是否VIP:0否 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 同步)';