diff --git a/src/main/java/com/rj/controller/LbPurchaseApplyController.java b/src/main/java/com/rj/controller/LbPurchaseApplyController.java index 5cf2464..96442bb 100644 --- a/src/main/java/com/rj/controller/LbPurchaseApplyController.java +++ b/src/main/java/com/rj/controller/LbPurchaseApplyController.java @@ -8,6 +8,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; @@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletResponse; import java.time.LocalDate; +import java.util.List; import java.util.Map; @RestController @@ -26,6 +29,30 @@ public class LbPurchaseApplyController { @Autowired private ILbPurchaseApplyService lbPurchaseApplyService; + @Data + @Schema(description = "按用户ID批量开通或关闭 hxrd 用户特权请求体") + public static class UpdateHxrAdminUserVipRequest { + @Schema(description = "租户ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "租户id不能为空") + private String tenantId; + + @Schema(description = "hxrd 后台用户 ID 列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "用户id列表不能为空") + private List userIds; + + @Schema(description = "是否会员:1 开通特权,0 关闭特权", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "isVip不能为空") + private Integer isVip; + + @Schema(description = "特权结束时间,格式 yyyy-MM-dd HH:mm:ss;开通时必填") + private String vipTime; + + @Schema(description = "最大订单数 max_order;传 0 时不向第三方提交该字段,保持原值", + requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "maxOrder不能为空") + private Integer maxOrder; + } + @Data @Schema(description = "大模型解析订货信息请求体") public static class ParseOrderInfoRequest { @@ -131,6 +158,24 @@ public class LbPurchaseApplyController { return ResponseEntity.badRequest().body(result); } + @PostMapping("/updateHxrAdminUserVipByUserIds") + @Operation(summary = "按用户ID列表批量开通或关闭 hxrd 用户特权", + description = "根据入参 userIds 逐条调用 POST /app/admin/user/update," + + "不计算 viptime,直接使用请求体中的 vipTime、maxOrder、isVip(1 开通,0 关闭);" + + "maxOrder 为 0 时不传 max_order,不修改第三方该字段。" + + "需配置 hxr.admin.cookie 或 hxr.admin.phpsid。") + public ResponseEntity> updateHxrAdminUserVipByUserIds( + @Parameter(description = "租户、用户列表及特权参数", required = true) + @Valid @RequestBody UpdateHxrAdminUserVipRequest body) { + Map result = lbPurchaseApplyService.updateHxrAdminUserVipByUserIds( + body.getTenantId(), body.getUserIds(), body.getIsVip(), body.getVipTime(), body.getMaxOrder()); + Boolean success = (Boolean) result.get("success"); + if (success != null && success) { + return ResponseEntity.ok(result); + } + return ResponseEntity.badRequest().body(result); + } + @GetMapping("/syncHxrAdminColleagueVip") @Operation(summary = "按申请日期与租户同步同事 hxrd 用户 VIP", description = "根据 lb_purchase_apply 的申请日期区间与 tenant_id 逐条处理," diff --git a/src/main/java/com/rj/service/HxrAdminUserService.java b/src/main/java/com/rj/service/HxrAdminUserService.java index 638d4b2..773f76d 100644 --- a/src/main/java/com/rj/service/HxrAdminUserService.java +++ b/src/main/java/com/rj/service/HxrAdminUserService.java @@ -102,7 +102,7 @@ public class HxrAdminUserService { } /** - * 分页拉取用户列表,{@code data} 解析为 {@link LbUser}(与 lb_user 字段对应)。 + * 分页拉取用户列表,{@code data} 解析为 {@link }(与 lb_user 字段对应)。 * * @param page 页码,从 1 开始 */ @@ -225,13 +225,25 @@ public class HxrAdminUserService { * @return 接口 {@code code==0} 时为 true */ public boolean updateUserVipFields(long userId, int maxOrder, String viptimeFormatted) throws Exception { + return updateUserVipFields(userId, maxOrder, viptimeFormatted, 1); + } + + /** + * 更新用户 VIP 相关字段({@code application/x-www-form-urlencoded})。 + * + * @param maxOrder 为 null 时不传 max_order,第三方保持原值 + * @param isVip 1 开通特权,0 关闭特权 + * @return 接口 {@code code==0} 时为 true + */ + public boolean updateUserVipFields(long userId, Integer maxOrder, String viptimeFormatted, int isVip) + throws Exception { String cookieHeader = properties.resolveCookieHeader(); if (cookieHeader == null || cookieHeader.isBlank()) { log.warn("hxr.admin 未配置 cookie 或 phpsid,跳过 user/update"); return false; } - String form = buildUpdateFormBody(userId, maxOrder, viptimeFormatted); + String form = buildUpdateFormBody(userId, maxOrder, viptimeFormatted, isVip); HttpClient client = HttpClient.newBuilder() .connectTimeout(java.time.Duration.ofSeconds(35)) @@ -275,13 +287,19 @@ public class HxrAdminUserService { return t.format(VIP_TIME_FORMAT); } - private static String buildUpdateFormBody(long userId, int maxOrder, String viptimeFormatted) { - return "id=" + userId - + "&max_order=" + maxOrder - + "&is_vip=1" - + "&is_resell=1" - + "&status=1" - + "&viptime=" + URLEncoder.encode(viptimeFormatted, StandardCharsets.UTF_8); + private static String buildUpdateFormBody(long userId, Integer maxOrder, String viptimeFormatted, int isVip) { + int vipFlag = isVip == 1 ? 1 : 0; + String viptime = viptimeFormatted != null ? viptimeFormatted : ""; + StringBuilder form = new StringBuilder(); + form.append("id=").append(userId); + if (maxOrder != null) { + form.append("&max_order=").append(maxOrder); + } + form.append("&is_vip=").append(vipFlag) + .append("&is_resell=1") + .append("&status=1") + .append("&viptime=").append(URLEncoder.encode(viptime, StandardCharsets.UTF_8)); + return form.toString(); } private static String abbreviate(String s, int maxLen) { diff --git a/src/main/java/com/rj/service/ILbPurchaseApplyService.java b/src/main/java/com/rj/service/ILbPurchaseApplyService.java index bd2defd..4766eb3 100644 --- a/src/main/java/com/rj/service/ILbPurchaseApplyService.java +++ b/src/main/java/com/rj/service/ILbPurchaseApplyService.java @@ -5,6 +5,7 @@ import com.rj.entity.LbPurchaseApply; import jakarta.servlet.http.HttpServletResponse; import java.time.LocalDate; +import java.util.List; import java.util.Map; public interface ILbPurchaseApplyService extends IService { @@ -34,6 +35,12 @@ public interface ILbPurchaseApplyService extends IService { Map syncHxrAdminUserVipByApplyDateRange( LocalDate applyDateStart, LocalDate applyDateEnd, String tenantId, Integer maxOrder); + /** + * 按用户 ID 列表批量调用 hxrd 用户更新接口,开通或关闭特权(不计算 viptime,使用入参)。 + */ + Map updateHxrAdminUserVipByUserIds( + String tenantId, List userIds, Integer isVip, String vipTime, Integer maxOrder); + /** * 按申请日期区间与租户筛选进货申请,对每条记录的 {@code colleague_phone} 调用 hxrd 用户查询与更新, * 仅当 {@code colleague_name} 能在 {@code lb_daily_user_trade}(同租户、按 nickname)中查到时才开通; diff --git a/src/main/java/com/rj/service/impl/LbPurchaseApplyServiceImpl.java b/src/main/java/com/rj/service/impl/LbPurchaseApplyServiceImpl.java index 75733f7..7e97709 100644 --- a/src/main/java/com/rj/service/impl/LbPurchaseApplyServiceImpl.java +++ b/src/main/java/com/rj/service/impl/LbPurchaseApplyServiceImpl.java @@ -464,7 +464,7 @@ public class LbPurchaseApplyServiceImpl /** * 按租户 + 手机号关联 {@link LbUser}:申请人 {@code applyPhone}、推荐人 {@code colleaguePhone} 分别对应 - * {@link LbPurchaseApply#applicantUser}、{@link LbPurchaseApply#referrerUser}(仅返回脱敏后的用户字段)。 + * {@link LbPurchaseApply#}、{@link LbPurchaseApply#}(仅返回脱敏后的用户字段)。 */ private void fillLinkedLbUsers(List records) { if (records == null || records.isEmpty()) { @@ -521,6 +521,7 @@ public class LbPurchaseApplyServiceImpl view.setTodayBuyTotal(source.getTodayBuyTotal()); view.setTodaySellTotal(source.getTodaySellTotal()); view.setJoinTime(source.getJoinTime()); + view.setMaxOrder(source.getMaxOrder()); return view; } @@ -733,6 +734,93 @@ public class LbPurchaseApplyServiceImpl } } + @Override + public Map updateHxrAdminUserVipByUserIds( + String tenantId, List userIds, Integer isVip, String vipTime, Integer maxOrder) { + Map result = new HashMap<>(); + try { + if (tenantId == null || tenantId.trim().isEmpty()) { + result.put("success", false); + result.put("message", "租户id不能为空"); + return result; + } + if (userIds == null || userIds.isEmpty()) { + result.put("success", false); + result.put("message", "用户id列表不能为空"); + return result; + } + if (isVip == null || (isVip != 0 && isVip != 1)) { + result.put("success", false); + result.put("message", "isVip 必须为 0 或 1"); + return result; + } + if (maxOrder == null) { + result.put("success", false); + result.put("message", "maxOrder不能为空"); + return result; + } + String vipTimeStr = vipTime != null ? vipTime.trim() : ""; + if (isVip == 1 && vipTimeStr.isEmpty()) { + result.put("success", false); + result.put("message", "开通特权时 vipTime 不能为空"); + return result; + } + + List> details = new ArrayList<>(); + int ok = 0; + int fail = 0; + for (Long userId : userIds) { + Map one = new HashMap<>(); + one.put("userId", userId); + one.put("isVip", isVip); + one.put("viptime", vipTimeStr); + one.put("maxOrder", maxOrder); + if (userId == null) { + one.put("success", false); + one.put("message", "用户id为空,跳过"); + fail++; + details.add(one); + continue; + } + try { + Integer maxOrderForApi = maxOrder == 0 ? null : maxOrder; + boolean updated = + hxrAdminUserService.updateUserVipFields(userId, maxOrderForApi, vipTimeStr, isVip); + if (updated) { + one.put("success", true); + one.put("message", "user/update 成功"); + ok++; + } else { + one.put("success", false); + one.put("message", "user/update 失败"); + fail++; + } + } catch (Exception ex) { + one.put("success", false); + one.put("message", "调用异常:" + ex.getMessage()); + fail++; + } + details.add(one); + } + + result.put("success", fail == 0); + result.put("message", fail == 0 ? "全部处理成功" : "部分或全部失败,见 details"); + result.put("totalRecords", userIds.size()); + result.put("successCount", ok); + result.put("failCount", fail); + result.put("isVip", isVip); + result.put("vipTime", vipTimeStr); + result.put("maxOrder", maxOrder); + result.put("details", details); + result.put("tenantId", tenantId.trim()); + return result; + } catch (Exception e) { + result.put("success", false); + result.put("message", "处理异常:" + e.getMessage()); + return result; + } + } + @Override public Map syncHxrAdminColleagueVipByApplyDateRange( LocalDate applyDateStart, LocalDate applyDateEnd, String tenantId) {