Ver Fonte

1.完成后台履约者相关模块功能开发
2.完成app端履约者入驻功能开发
3.完成app端履约者登录以及个人中心功能开发

steelwei há 1 mês atrás
pai
commit
71d57eadfe
55 ficheiros alterados com 3439 adições e 29 exclusões
  1. 12 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteAreaStationService.java
  2. 17 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java
  3. 5 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java
  4. 24 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteAreaStationVo.java
  5. 6 1
      ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java
  6. 1 11
      ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java
  7. 1 0
      ruoyi-common/yingpaipay-common-platform/src/main/java/org/dromara/common/platform/Platform.java
  8. 3 2
      ruoyi-gateway/src/main/java/org/dromara/gateway/filter/WebCorsFilter.java
  9. 19 12
      ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/controller/SysSmsController.java
  10. 28 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteAreaStationServiceImpl.java
  11. 35 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java
  12. 2 1
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTag.java
  13. 2 1
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTagRel.java
  14. 84 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfAppController.java
  15. 84 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfAuditController.java
  16. 247 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfFulfillerController.java
  17. 82 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfLogController.java
  18. 111 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfTagController.java
  19. 147 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfAudit.java
  20. 70 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfBalanceLog.java
  21. 178 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfFulfiller.java
  22. 70 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfPointsLog.java
  23. 70 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfRewardLog.java
  24. 68 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/SysTag.java
  25. 45 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/SysTagRel.java
  26. 40 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfAdjustBalanceBo.java
  27. 40 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfAdjustPointsBo.java
  28. 129 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfAuditBo.java
  29. 130 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfFulfillerBo.java
  30. 46 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfRewardBo.java
  31. 55 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfTagBo.java
  32. 75 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfAuditVo.java
  33. 34 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfBalanceLogVo.java
  34. 79 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfFulfillerVo.java
  35. 34 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfPointsLogVo.java
  36. 34 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfRewardLogVo.java
  37. 35 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/SysTagVo.java
  38. 15 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfAuditMapper.java
  39. 15 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfBalanceLogMapper.java
  40. 15 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfFulfillerMapper.java
  41. 15 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfPointsLogMapper.java
  42. 15 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfRewardLogMapper.java
  43. 15 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/SysTagMapper.java
  44. 14 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/SysTagRelMapper.java
  45. 46 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfAuditService.java
  46. 123 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfFulfillerService.java
  47. 49 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfTagService.java
  48. 247 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfAuditServiceImpl.java
  49. 524 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfFulfillerServiceImpl.java
  50. 92 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfTagServiceImpl.java
  51. 1 1
      ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties
  52. 11 0
      script/config/nacos/application-common.yml
  53. 35 0
      script/config/nacos/yingpaipay-archieves.yml
  54. 40 0
      script/config/nacos/yingpaipay-fulfiller.yml
  55. 25 0
      script/config/nacos/yingpaipay-service.yml

+ 12 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteAreaStationService.java

@@ -1,5 +1,17 @@
 package org.dromara.system.api;
 
+import org.dromara.system.api.domain.vo.RemoteAreaStationVo;
+
+import java.util.List;
+
 public interface RemoteAreaStationService {
     String selectNameById(Long id);
+
+    /**
+     * 查询子级区域/站点列表
+     *
+     * @param parentId 父级ID,null则查询顶级
+     * @return 子级列表
+     */
+    List<RemoteAreaStationVo> listByParentId(Long parentId);
 }

+ 17 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java

@@ -69,6 +69,14 @@ public interface RemoteUserService {
      */
     Boolean registerUserInfo(RemoteUserBo remoteUserBo) throws UserException, ServiceException;
 
+    /**
+     * 直接创建用户(不检查注册开关,用于后台审核通过等场景)
+     *
+     * @param remoteUserBo 用户信息
+     * @return 创建后的用户ID
+     */
+    Long createUser(RemoteUserBo remoteUserBo) throws UserException, ServiceException;
+
     /**
      * 通过userId查询用户账户
      *
@@ -165,4 +173,13 @@ public interface RemoteUserService {
      */
     Map<Long, String> selectUserNamesByIds(List<Long> userIds);
 
+    /**
+     * 重置用户密码(密码需已BCrypt加密)
+     *
+     * @param userId   用户ID
+     * @param password BCrypt加密后的密码
+     * @return 结果
+     */
+    int resetUserPwd(Long userId, String password);
+
 }

+ 5 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java

@@ -103,6 +103,11 @@ public class RemoteUserBo implements Serializable {
      */
     private Date loginDate;
 
+    /**
+     * 平台ID
+     */
+    private Integer platformId;
+
     /**
      * 备注
      */

+ 24 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteAreaStationVo.java

@@ -0,0 +1,24 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 区域站点远程调用VO
+ *
+ * @author steelwei
+ */
+@Data
+public class RemoteAreaStationVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private String name;
+    private Long parentId;
+    private Integer type;
+    private Integer status;
+}

+ 6 - 1
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java

@@ -21,7 +21,12 @@ public enum UserType {
     /**
      * 移动客户端用户
      */
-    APP_USER("app_user");
+    APP_USER("app_user"),
+
+    /**
+     * 履约者用户
+     */
+    FULFILLER_USER("flf_user");
 
     /**
      * 用户类型标识(用于 token、权限识别等)

+ 1 - 11
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java

@@ -4,15 +4,12 @@ import cn.hutool.core.util.ObjectUtil;
 import jakarta.servlet.*;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-import org.dromara.common.core.constant.HttpStatus;
-import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.encrypt.annotation.ApiEncrypt;
 import org.dromara.common.encrypt.properties.ApiDecryptProperties;
 import org.springframework.http.HttpMethod;
 import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.HandlerExceptionResolver;
 import org.springframework.web.servlet.HandlerExecutionChain;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 
@@ -50,14 +47,7 @@ public class CryptoFilter implements Filter {
                 // 请求解密
                 requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
             } else {
-                // 是否有注解,有就报错,没有放行
-                if (ObjectUtil.isNotNull(apiEncrypt)) {
-                    HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
-                    exceptionResolver.resolveException(
-                        servletRequest, servletResponse, null,
-                        new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
-                    return;
-                }
+                // 没有加密标头时直接放行(兼容不支持加密的移动端客户端)
             }
         }
 

+ 1 - 0
ruoyi-common/yingpaipay-common-platform/src/main/java/org/dromara/common/platform/Platform.java

@@ -22,6 +22,7 @@ public enum Platform {
 
     ADMIN(0, "4pwuAzDBzUd6hekvGHHKedT4VX5WHERAXHpeztPFAzRaUsBUrD", "管理后台"),
     MERCHANT(1, "MfJkMNMW2JKXBuPcbP2rxkD3ynXmReAZZFm4fN7cAGwGJdKCmd", "商户后台"),
+    FULFILLER(2, "FlfAppPlatformCodeX9kR7mT3wQ5vZ8nB1jY6pD4sL0hC2gA", "履约者App"),
     ;
 
     private final Integer id;

+ 3 - 2
ruoyi-gateway/src/main/java/org/dromara/gateway/filter/WebCorsFilter.java

@@ -28,7 +28,7 @@ public class WebCorsFilter implements WebFilter, Ordered {
     private static final String ALLOWED_HEADERS =
         "X-Requested-With, Content-Language, Content-Type, " +
         "Authorization, clientid, credential, X-XSRF-TOKEN, " +
-        "isToken, token, Admin-Token, App-Token, Encrypt-Key, isEncrypt";
+        "isToken, token, Admin-Token, App-Token, Encrypt-Key, isEncrypt, X-Platform-Code";
 
     /**
      * 允许的请求方法
@@ -66,7 +66,8 @@ public class WebCorsFilter implements WebFilter, Ordered {
             HttpHeaders headers = response.getHeaders();
             headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
             headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
-            headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
+            String origin = request.getHeaders().getOrigin();
+            headers.add("Access-Control-Allow-Origin", origin != null ? origin : ALLOWED_ORIGIN);
             headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
             headers.add("Access-Control-Max-Age", MAX_AGE);
             headers.add("Access-Control-Allow-Credentials", "true");

+ 19 - 12
ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/controller/SysSmsController.java

@@ -45,22 +45,29 @@ public class SysSmsController extends BaseController {
      */
     @RateLimiter(key = "#phonenumber", time = 60, count = 1)
     @GetMapping("/code")
-    public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
+    public R<String> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
         String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
         String code = RandomUtil.randomNumbers(4);
         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
-        // 验证码模板id 自行处理 (查数据库或写死均可)
-        String templateId = "";
-        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
-        map.put("code", code);
-        SysSmsConfig config = smsConfigService.getAvailableConfig();
-        SmsBlend smsBlend = SmsFactory.getSmsBlend(String.valueOf(config.getId()));
-        SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, config.getTemplateId(), map);
-        if (!smsResponse.isSuccess()) {
-            log.error("验证码短信发送异常 => {}", smsResponse);
-            return R.fail(smsResponse.getData().toString());
+
+        // 尝试通过短信服务商发送
+        try {
+            LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
+            map.put("code", code);
+            SysSmsConfig config = smsConfigService.getAvailableConfig();
+            SmsBlend smsBlend = SmsFactory.getSmsBlend(String.valueOf(config.getId()));
+            SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, config.getTemplateId(), map);
+            if (!smsResponse.isSuccess()) {
+                log.error("验证码短信发送异常 => {}", smsResponse);
+                // TODO 【生产环境必须删除】短信发送失败时将验证码返回前端,仅限开发测试使用
+                return R.ok("短信发送失败,开发模式返回验证码", code);
+            }
+            return R.ok();
+        } catch (Exception e) {
+            log.warn("[DEV] 短信服务商未配置,验证码直接返回前端: phone={}, code={}", phonenumber, code);
+            // TODO 【生产环境必须删除此分支】短信服务商未配置时将验证码返回前端,仅限开发测试使用
+            return R.ok("短信服务未配置,开发模式返回验证码", code);
         }
-        return R.ok();
     }
 
 }

+ 28 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteAreaStationServiceImpl.java

@@ -1,13 +1,19 @@
 package org.dromara.system.dubbo;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.system.api.RemoteAreaStationService;
+import org.dromara.system.api.domain.vo.RemoteAreaStationVo;
+import org.dromara.system.domain.SysAreaStation;
 import org.dromara.system.mapper.SysAreaStationMapper;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 @DubboService
 @Service
 @RequiredArgsConstructor
@@ -20,4 +26,26 @@ public class RemoteAreaStationServiceImpl implements RemoteAreaStationService {
     public String selectNameById(Long id) {
         return baseMapper.selectById(id).getName();
     }
+
+    @Override
+    public List<RemoteAreaStationVo> listByParentId(Long parentId) {
+        LambdaQueryWrapper<SysAreaStation> lqw = Wrappers.lambdaQuery();
+        if (parentId == null || parentId == 0) {
+            lqw.isNull(SysAreaStation::getParentId)
+                .or().eq(SysAreaStation::getParentId, 0);
+        } else {
+            lqw.eq(SysAreaStation::getParentId, parentId);
+        }
+        lqw.eq(SysAreaStation::getStatus, 0);
+        lqw.orderByAsc(SysAreaStation::getSort);
+        return baseMapper.selectList(lqw).stream().map(e -> {
+            RemoteAreaStationVo vo = new RemoteAreaStationVo();
+            vo.setId(e.getId());
+            vo.setName(e.getName());
+            vo.setParentId(e.getParentId());
+            vo.setType(e.getType());
+            vo.setStatus(e.getStatus());
+            return vo;
+        }).toList();
+    }
 }

+ 35 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java

@@ -203,6 +203,36 @@ public class RemoteUserServiceImpl implements RemoteUserService {
         return userService.registerUser(sysUserBo, remoteUserBo.getTenantId());
     }
 
+    /**
+     * 直接创建用户(不检查注册开关,用于后台审核通过等场景)
+     *
+     * @param remoteUserBo 用户信息
+     * @return 创建后的用户ID
+     */
+    @Override
+    public Long createUser(RemoteUserBo remoteUserBo) throws UserException, ServiceException {
+        SysUserBo sysUserBo = MapstructUtils.convert(remoteUserBo, SysUserBo.class);
+        String tenantId = remoteUserBo.getTenantId();
+        boolean exist = TenantHelper.dynamic(tenantId, () ->
+            userMapper.exists(new LambdaQueryWrapper<SysUser>()
+                .eq(SysUser::getUserName, sysUserBo.getUserName())
+                .eq(SysUser::getPlatformId, remoteUserBo.getPlatformId()))
+        );
+        if (exist) {
+            throw new UserException("user.register.save.error", sysUserBo.getUserName());
+        }
+        sysUserBo.setCreateBy(0L);
+        sysUserBo.setUpdateBy(0L);
+        SysUser sysUser = MapstructUtils.convert(sysUserBo, SysUser.class);
+        sysUser.setTenantId(tenantId);
+        sysUser.setPlatformId(remoteUserBo.getPlatformId());
+        TenantHelper.dynamic(tenantId, () -> {
+            userMapper.insert(sysUser);
+            return null;
+        });
+        return sysUser.getUserId();
+    }
+
     /**
      * 通过用户ID查询用户账户
      *
@@ -417,4 +447,9 @@ public class RemoteUserServiceImpl implements RemoteUserService {
         return StreamUtils.toMap(list, SysUser::getUserId, SysUser::getNickName);
     }
 
+    @Override
+    public int resetUserPwd(Long userId, String password) {
+        return userService.resetUserPwd(userId, password);
+    }
+
 }

+ 2 - 1
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTag.java

@@ -15,7 +15,8 @@ import java.io.Serial;
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName("sys_tag")
+@TableName(value = "sys_tag", autoResultMap = true)
+@InterceptorIgnore(tenantLine = "true")
 public class SysTag extends BaseEntity {
 
     @Serial

+ 2 - 1
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTagRel.java

@@ -13,7 +13,8 @@ import java.io.Serializable;
  * @date 2026-02-27
  */
 @Data
-@TableName("sys_tag_rel")
+@TableName(value = "sys_tag_rel", autoResultMap = true)
+@InterceptorIgnore(tenantLine = "true")
 public class SysTagRel implements Serializable {
 
     @Serial

+ 84 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfAppController.java

@@ -0,0 +1,84 @@
+package org.dromara.fulfiller.controller;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.fulfiller.domain.bo.FlfAuditBo;
+import org.dromara.fulfiller.service.IFlfAuditService;
+import org.dromara.resource.api.RemoteFileService;
+import org.dromara.resource.api.domain.RemoteFile;
+import org.dromara.system.api.RemoteAreaStationService;
+import org.dromara.system.api.domain.vo.RemoteAreaStationVo;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 履约者App公开接口(无需登录)
+ * 用于招募表单提交、区域站点查询等
+ *
+ * @author steelwei
+ */
+@Slf4j
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/app")
+public class FlfAppController {
+
+    private final IFlfAuditService auditService;
+
+    @DubboReference
+    private RemoteAreaStationService remoteAreaStationService;
+
+    @DubboReference
+    private RemoteFileService remoteFileService;
+
+    /**
+     * 查询子级区域/站点列表(级联选择器用)
+     *
+     * @param parentId 父级ID,不传或0则查询顶级
+     */
+    @GetMapping("/area/children")
+    public R<List<RemoteAreaStationVo>> areaChildren(
+        @RequestParam(required = false, defaultValue = "0") Long parentId) {
+        return R.ok(remoteAreaStationService.listByParentId(parentId));
+    }
+
+    /**
+     * 公开文件上传(招募流程用,无需登录)
+     */
+    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<Map<String, Object>> upload(@RequestPart("file") MultipartFile file) {
+        try {
+            RemoteFile remoteFile = remoteFileService.upload(
+                file.getName(), file.getOriginalFilename(),
+                file.getContentType(), file.getBytes()
+            );
+            Map<String, Object> result = new HashMap<>();
+            result.put("url", remoteFile.getUrl());
+            result.put("fileName", remoteFile.getOriginalName());
+            result.put("ossId", String.valueOf(remoteFile.getOssId()));
+            return R.ok(result);
+        } catch (Exception e) {
+            log.error("文件上传失败", e);
+            return R.fail("文件上传失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 提交入驻申请(App端,无需登录)
+     */
+    @PostMapping("/audit/submit")
+    public R<Void> submitAudit(@Validated(AddGroup.class) @RequestBody FlfAuditBo bo) {
+        bo.setType("register");
+        return auditService.submitApply(bo) ? R.ok() : R.fail("提交失败");
+    }
+}

+ 84 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfAuditController.java

@@ -0,0 +1,84 @@
+package org.dromara.fulfiller.controller;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.fulfiller.domain.vo.FlfAuditVo;
+import org.dromara.fulfiller.domain.bo.FlfAuditBo;
+import org.dromara.fulfiller.service.IFlfAuditService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 履约者审核管理
+ * 前端访问路由地址为:/fulfiller/audit
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/audit")
+public class FlfAuditController extends BaseController {
+
+    private final IFlfAuditService auditService;
+
+    /**
+     * 查询审核记录列表
+     */
+    @SaCheckPermission("fulfiller:audit:list")
+    @GetMapping("/list")
+    public TableDataInfo<FlfAuditVo> list(FlfAuditBo bo, PageQuery pageQuery) {
+        return auditService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询待审核数量
+     */
+    @SaCheckPermission("fulfiller:audit:list")
+    @GetMapping("/pendingCount")
+    public R<Long> pendingCount() {
+        return R.ok(auditService.countPending());
+    }
+
+    /**
+     * 获取审核记录详细信息
+     */
+    @SaCheckPermission("fulfiller:audit:query")
+    @GetMapping("/{id}")
+    public R<FlfAuditVo> getInfo(@NotNull(message = "主键不能为空")
+                                  @PathVariable("id") Long id) {
+        return R.ok(auditService.queryById(id));
+    }
+
+    /**
+     * 审核通过
+     */
+    @SaCheckPermission("fulfiller:audit:edit")
+    @Log(title = "履约者审核", businessType = BusinessType.UPDATE)
+    @PutMapping("/pass/{id}")
+    public R<Void> pass(@NotNull(message = "主键不能为空")
+                         @PathVariable("id") Long id) {
+        return toAjax(auditService.pass(id));
+    }
+
+    /**
+     * 审核驳回
+     */
+    @SaCheckPermission("fulfiller:audit:edit")
+    @Log(title = "履约者审核", businessType = BusinessType.UPDATE)
+    @PutMapping("/reject/{id}")
+    public R<Void> reject(@NotNull(message = "主键不能为空")
+                           @PathVariable("id") Long id,
+                           @RequestParam String rejectReason) {
+        return toAjax(auditService.reject(id, rejectReason));
+    }
+
+}

+ 247 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfFulfillerController.java

@@ -0,0 +1,247 @@
+package org.dromara.fulfiller.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.fulfiller.domain.vo.FlfFulfillerVo;
+import org.dromara.fulfiller.domain.bo.FlfFulfillerBo;
+import org.dromara.fulfiller.domain.bo.FlfRewardBo;
+import org.dromara.fulfiller.domain.bo.FlfAdjustPointsBo;
+import org.dromara.fulfiller.domain.bo.FlfAdjustBalanceBo;
+import org.dromara.fulfiller.service.IFlfFulfillerService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 履约者管理
+ * 前端访问路由地址为:/fulfiller/fulfiller
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/fulfiller")
+public class FlfFulfillerController extends BaseController {
+
+    private final IFlfFulfillerService fulfillerService;
+
+    /**
+     * 获取当前登录履约者个人信息(App端,只需登录无需后台权限)
+     */
+    @GetMapping("/my")
+    public R<FlfFulfillerVo> getMyProfile() {
+        Long userId = LoginHelper.getUserId();
+        return R.ok(fulfillerService.queryByUserId(userId));
+    }
+
+    /**
+     * 修改个人头像(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-修改头像", businessType = BusinessType.UPDATE)
+    @PutMapping("/my/avatar")
+    public R<Void> updateMyAvatar(@RequestBody Map<String, String> params) {
+        Long userId = LoginHelper.getUserId();
+        return toAjax(fulfillerService.updateAvatarByUserId(userId, params.get("avatar")));
+    }
+
+    /**
+     * 修改真实姓名(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-修改姓名", businessType = BusinessType.UPDATE)
+    @PutMapping("/my/name")
+    public R<Void> updateMyName(@RequestBody Map<String, String> params) {
+        Long userId = LoginHelper.getUserId();
+        return toAjax(fulfillerService.updateNameByUserId(userId, params.get("name")));
+    }
+
+    /**
+     * 修改工作状态(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-修改工作状态", businessType = BusinessType.UPDATE)
+    @PutMapping("/my/status")
+    public R<Void> updateMyStatus(@RequestBody Map<String, String> params) {
+        Long userId = LoginHelper.getUserId();
+        return toAjax(fulfillerService.updateStatusByUserId(userId, params.get("status")));
+    }
+
+    /**
+     * 修改工作城市(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-修改工作城市", businessType = BusinessType.UPDATE)
+    @PutMapping("/my/city")
+    public R<Void> updateMyCity(@RequestBody Map<String, String> params) {
+        Long userId = LoginHelper.getUserId();
+        return toAjax(fulfillerService.updateCityByUserId(userId, params.get("cityCode"), params.get("cityName")));
+    }
+
+    /**
+     * 获取认证信息(App端)
+     * @author steelwei
+     */
+    @GetMapping("/my/auth")
+    public R<FlfFulfillerVo> getMyAuthInfo() {
+        Long userId = LoginHelper.getUserId();
+        return R.ok(fulfillerService.queryByUserId(userId));
+    }
+
+    /**
+     * 修改手机号(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-修改手机号", businessType = BusinessType.UPDATE)
+    @PutMapping("/my/phone")
+    public R<Void> updateMyPhone(@RequestBody Map<String, String> params) {
+        Long userId = LoginHelper.getUserId();
+        // TODO: 验证验证码
+        return toAjax(fulfillerService.updatePhoneByUserId(userId, params.get("phone")));
+    }
+
+    /**
+     * 修改密码(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-修改密码", businessType = BusinessType.UPDATE)
+    @PutMapping("/my/password")
+    public R<Void> updateMyPassword(@RequestBody Map<String, String> params) {
+        Long userId = LoginHelper.getUserId();
+        return toAjax(fulfillerService.updatePasswordByUserId(userId, params.get("oldPassword"), params.get("newPassword")));
+    }
+
+    /**
+     * 注销账号(App端)
+     * @author steelwei
+     */
+    @Log(title = "履约者-注销账号", businessType = BusinessType.DELETE)
+    @DeleteMapping("/my/account")
+    public R<Void> deleteMyAccount() {
+        Long userId = LoginHelper.getUserId();
+        return toAjax(fulfillerService.deleteAccountByUserId(userId));
+    }
+
+    /**
+     * 查询履约者列表
+     */
+    @SaCheckPermission("fulfiller:fulfiller:list")
+    @GetMapping("/list")
+    public TableDataInfo<FlfFulfillerVo> list(FlfFulfillerBo bo, PageQuery pageQuery) {
+        return fulfillerService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出履约者列表
+     */
+    @SaCheckPermission("fulfiller:fulfiller:export")
+    @Log(title = "履约者管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(FlfFulfillerBo bo, HttpServletResponse response) {
+        List<FlfFulfillerVo> list = fulfillerService.queryList(bo);
+        ExcelUtil.exportExcel(list, "履约者管理", FlfFulfillerVo.class, response);
+    }
+
+    /**
+     * 获取履约者详细信息
+     */
+    @SaCheckPermission("fulfiller:fulfiller:query")
+    @GetMapping("/{id}")
+    public R<FlfFulfillerVo> getInfo(@NotNull(message = "主键不能为空")
+                                      @PathVariable("id") Long id) {
+        return R.ok(fulfillerService.queryById(id));
+    }
+
+    /**
+     * 新增履约者
+     */
+    @SaCheckPermission("fulfiller:fulfiller:add")
+    @Log(title = "履约者管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody FlfFulfillerBo bo) {
+        return toAjax(fulfillerService.insertByBo(bo));
+    }
+
+    /**
+     * 修改履约者
+     */
+    @SaCheckPermission("fulfiller:fulfiller:edit")
+    @Log(title = "履约者管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlfFulfillerBo bo) {
+        return toAjax(fulfillerService.updateByBo(bo));
+    }
+
+    /**
+     * 切换状态
+     */
+    @SaCheckPermission("fulfiller:fulfiller:edit")
+    @Log(title = "履约者管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestParam Long id, @RequestParam String status) {
+        return toAjax(fulfillerService.changeStatus(id, status));
+    }
+
+    /**
+     * 重置密码
+     */
+    @SaCheckPermission("fulfiller:fulfiller:edit")
+    @Log(title = "履约者管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/resetPwd")
+    public R<Void> resetPwd(@RequestParam Long id, @RequestParam String password) {
+        return toAjax(fulfillerService.resetPwd(id, password));
+    }
+
+    /**
+     * 奖惩操作
+     */
+    @SaCheckPermission("fulfiller:fulfiller:edit")
+    @Log(title = "履约者管理-奖惩", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PostMapping("/reward")
+    public R<Void> reward(@Validated @RequestBody FlfRewardBo bo) {
+        return toAjax(fulfillerService.reward(bo));
+    }
+
+    /**
+     * 调整积分
+     */
+    @SaCheckPermission("fulfiller:fulfiller:edit")
+    @Log(title = "履约者管理-调整积分", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PostMapping("/adjustPoints")
+    public R<Void> adjustPoints(@Validated @RequestBody FlfAdjustPointsBo bo) {
+        return toAjax(fulfillerService.adjustPoints(bo));
+    }
+
+    /**
+     * 调整余额
+     */
+    @SaCheckPermission("fulfiller:fulfiller:edit")
+    @Log(title = "履约者管理-调整余额", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PostMapping("/adjustBalance")
+    public R<Void> adjustBalance(@Validated @RequestBody FlfAdjustBalanceBo bo) {
+        return toAjax(fulfillerService.adjustBalance(bo));
+    }
+
+}

+ 82 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfLogController.java

@@ -0,0 +1,82 @@
+package org.dromara.fulfiller.controller;
+
+import lombok.RequiredArgsConstructor;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.fulfiller.domain.vo.FlfPointsLogVo;
+import org.dromara.fulfiller.domain.vo.FlfBalanceLogVo;
+import org.dromara.fulfiller.domain.vo.FlfRewardLogVo;
+import org.dromara.fulfiller.domain.FlfPointsLog;
+import org.dromara.fulfiller.domain.FlfBalanceLog;
+import org.dromara.fulfiller.domain.FlfRewardLog;
+import org.dromara.fulfiller.mapper.FlfPointsLogMapper;
+import org.dromara.fulfiller.mapper.FlfBalanceLogMapper;
+import org.dromara.fulfiller.mapper.FlfRewardLogMapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+/**
+ * 履约者日志查询
+ * 前端访问路由地址为:/fulfiller/log
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/log")
+public class FlfLogController extends BaseController {
+
+    private final FlfPointsLogMapper pointsLogMapper;
+    private final FlfBalanceLogMapper balanceLogMapper;
+    private final FlfRewardLogMapper rewardLogMapper;
+
+    /**
+     * 查询积分变动记录
+     */
+    @SaCheckPermission("fulfiller:fulfiller:query")
+    @GetMapping("/points")
+    public TableDataInfo<FlfPointsLogVo> pointsLog(
+        @RequestParam Long fulfillerId, PageQuery pageQuery) {
+        LambdaQueryWrapper<FlfPointsLog> lqw = Wrappers.lambdaQuery();
+        lqw.eq(FlfPointsLog::getFulfillerId, fulfillerId);
+        lqw.orderByDesc(FlfPointsLog::getCreateTime);
+        Page<FlfPointsLogVo> result = pointsLogMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询余额变动记录
+     */
+    @SaCheckPermission("fulfiller:fulfiller:query")
+    @GetMapping("/balance")
+    public TableDataInfo<FlfBalanceLogVo> balanceLog(
+        @RequestParam Long fulfillerId, PageQuery pageQuery) {
+        LambdaQueryWrapper<FlfBalanceLog> lqw = Wrappers.lambdaQuery();
+        lqw.eq(FlfBalanceLog::getFulfillerId, fulfillerId);
+        lqw.orderByDesc(FlfBalanceLog::getCreateTime);
+        Page<FlfBalanceLogVo> result = balanceLogMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询奖惩记录
+     */
+    @SaCheckPermission("fulfiller:fulfiller:query")
+    @GetMapping("/reward")
+    public TableDataInfo<FlfRewardLogVo> rewardLog(
+        @RequestParam Long fulfillerId, PageQuery pageQuery) {
+        LambdaQueryWrapper<FlfRewardLog> lqw = Wrappers.lambdaQuery();
+        lqw.eq(FlfRewardLog::getFulfillerId, fulfillerId);
+        lqw.orderByDesc(FlfRewardLog::getCreateTime);
+        Page<FlfRewardLogVo> result = rewardLogMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+}

+ 111 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfTagController.java

@@ -0,0 +1,111 @@
+package org.dromara.fulfiller.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.fulfiller.domain.vo.SysTagVo;
+import org.dromara.fulfiller.domain.bo.FlfTagBo;
+import org.dromara.fulfiller.service.IFlfTagService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 标签管理(履约者模块)
+ * 前端访问路由地址为:/fulfiller/tag
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/tag")
+public class FlfTagController extends BaseController {
+
+    private final IFlfTagService tagService;
+
+    /**
+     * 查询标签列表
+     */
+    @SaCheckPermission("fulfiller:tag:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysTagVo> list(FlfTagBo bo, PageQuery pageQuery) {
+        return tagService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询标签列表(不分页,供选择器使用)
+     */
+    @GetMapping("/listAll")
+    public R<List<SysTagVo>> listAll(FlfTagBo bo) {
+        return R.ok(tagService.queryList(bo));
+    }
+
+    /**
+     * 导出标签列表
+     */
+    @SaCheckPermission("fulfiller:tag:export")
+    @Log(title = "标签管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(FlfTagBo bo, HttpServletResponse response) {
+        List<SysTagVo> list = tagService.queryList(bo);
+        ExcelUtil.exportExcel(list, "标签管理", SysTagVo.class, response);
+    }
+
+    /**
+     * 获取标签详细信息
+     */
+    @SaCheckPermission("fulfiller:tag:query")
+    @GetMapping("/{id}")
+    public R<SysTagVo> getInfo(@NotNull(message = "主键不能为空")
+                                @PathVariable("id") Long id) {
+        return R.ok(tagService.queryById(id));
+    }
+
+    /**
+     * 新增标签
+     */
+    @SaCheckPermission("fulfiller:tag:add")
+    @Log(title = "标签管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody FlfTagBo bo) {
+        return toAjax(tagService.insertByBo(bo));
+    }
+
+    /**
+     * 修改标签
+     */
+    @SaCheckPermission("fulfiller:tag:edit")
+    @Log(title = "标签管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlfTagBo bo) {
+        return toAjax(tagService.updateByBo(bo));
+    }
+
+    /**
+     * 删除标签
+     */
+    @SaCheckPermission("fulfiller:tag:remove")
+    @Log(title = "标签管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                           @PathVariable("ids") Long[] ids) {
+        return toAjax(tagService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+}

+ 147 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfAudit.java

@@ -0,0 +1,147 @@
+package org.dromara.fulfiller.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 履约者审核记录对象 flf_audit
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("flf_audit")
+public class FlfAudit extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 履约者ID(通过后关联)
+     */
+    private Long fulfillerId;
+
+    /**
+     * 审核类型 (register:入驻, qualification:资质变更)
+     */
+    private String type;
+
+    /**
+     * 申请人姓名
+     */
+    private String name;
+
+    /**
+     * 申请人手机号
+     */
+    private String phone;
+
+    /**
+     * 性别(0男 1女 2未知)
+     */
+    private String gender;
+
+    /**
+     * 出生日期
+     */
+    private Date birthday;
+
+    /**
+     * 工作类型 (full_time:全职, part_time:兼职)
+     */
+    private String workType;
+
+    /**
+     * 意向城市
+     */
+    private String city;
+
+    /**
+     * 意向地点
+     */
+    private String location;
+
+    /**
+     * 证件真实姓名
+     */
+    private String realName;
+
+    /**
+     * 身份证号
+     */
+    private String idCard;
+
+    /**
+     * 证件有效期至
+     */
+    private Date idValidDate;
+
+    /**
+     * 身份证人像面(OSS ID)
+     */
+    private Long idCardFront;
+
+    /**
+     * 身份证国徽面(OSS ID)
+     */
+    private Long idCardBack;
+
+    /**
+     * 专业资质图片(JSON数组)
+     */
+    private String qualifications;
+
+    /**
+     * 申请服务类型(JSON数组)
+     */
+    private String serviceTypes;
+
+    /**
+     * 申请时设置的密码(加密)
+     */
+    private String password;
+
+    /**
+     * 意向站点ID
+     */
+    private Long stationId;
+
+    /**
+     * 状态 (0:待审核, 1:已通过, 2:已驳回)
+     */
+    private Integer status;
+
+    /**
+     * 审核人ID
+     */
+    private Long auditBy;
+
+    /**
+     * 审核时间
+     */
+    private Date auditTime;
+
+    /**
+     * 驳回原因
+     */
+    private String rejectReason;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 70 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfBalanceLog.java

@@ -0,0 +1,70 @@
+package org.dromara.fulfiller.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 余额变动记录对象 flf_balance_log
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@TableName("flf_balance_log")
+public class FlfBalanceLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 履约者ID
+     */
+    private Long fulfillerId;
+
+    /**
+     * 变动方向 (add:增加, reduce:减少)
+     */
+    private String type;
+
+    /**
+     * 资金类型 (reward:奖励, punish:惩罚, salary:工资发放, withdraw:提现, other:其他)
+     */
+    private String subType;
+
+    /**
+     * 变动金额(分, 正数)
+     */
+    private Long amount;
+
+    /**
+     * 变动后余额(分)
+     */
+    private Long balanceAfter;
+
+    /**
+     * 备注说明
+     */
+    private String reason;
+
+    /**
+     * 操作人ID
+     */
+    private Long operatorId;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+}

+ 178 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfFulfiller.java

@@ -0,0 +1,178 @@
+package org.dromara.fulfiller.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 履约者信息对象 flf_fulfiller
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("flf_fulfiller")
+public class FlfFulfiller extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 关联系统用户ID
+     */
+    private Long userId;
+
+    /**
+     * 姓名
+     */
+    private String name;
+
+    /**
+     * 身份证真实姓名
+     */
+    private String realName;
+
+    /**
+     * 手机号/账号
+     */
+    private String phone;
+
+    /**
+     * 登录密码
+     */
+    private String password;
+
+    /**
+     * 性别(0男 1女 2未知)
+     */
+    private String gender;
+
+    /**
+     * 出生日期
+     */
+    private Date birthday;
+
+    /**
+     * 年龄
+     */
+    private Integer age;
+
+    /**
+     * 头像(OSS ID)
+     */
+    private String avatar;
+
+    /**
+     * 身份证号
+     */
+    private String idCard;
+
+    /**
+     * 身份证正面(OSS ID)
+     */
+    private Long idCardFront;
+
+    /**
+     * 身份证背面(OSS ID)
+     */
+    private Long idCardBack;
+
+    /**
+     * 身份证有效期至
+     */
+    private Date idCardExpiry;
+
+    /**
+     * 服务类型(JSON数组)
+     */
+    private String serviceTypes;
+
+    /**
+     * 服务城市编码
+     */
+    private String cityCode;
+
+    /**
+     * 服务城市名称
+     */
+    private String cityName;
+
+    /**
+     * 归属站点ID
+     */
+    private Long stationId;
+
+    /**
+     * 工作性质 (full_time:全职, part_time:兼职)
+     */
+    private String workType;
+
+    /**
+     * 等级ID
+     */
+    private Long levelId;
+
+    /**
+     * 当前积分
+     */
+    private Integer points;
+
+    /**
+     * 账户余额(分)
+     */
+    private Long balance;
+
+    /**
+     * 状态 (resting:休息, busy:接单中, disabled:禁用)
+     */
+    private String status;
+
+    /**
+     * 是否身份证认证
+     */
+    private Boolean authId;
+
+    /**
+     * 是否资质认证
+     */
+    private Boolean authQual;
+
+    /**
+     * 资质证书图片(JSON)
+     */
+    private String qualImages;
+
+    /**
+     * 服务单量
+     */
+    private Integer orderCount;
+
+    /**
+     * 拒单量
+     */
+    private Integer rejectCount;
+
+    /**
+     * 综合评分
+     */
+    private BigDecimal rating;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 70 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfPointsLog.java

@@ -0,0 +1,70 @@
+package org.dromara.fulfiller.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 积分变动记录对象 flf_points_log
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@TableName("flf_points_log")
+public class FlfPointsLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 履约者ID
+     */
+    private Long fulfillerId;
+
+    /**
+     * 变动类型 (add:增加, reduce:扣除)
+     */
+    private String type;
+
+    /**
+     * 业务类型 (order:订单, reward:奖励, punish:惩罚, adjust:手动调整)
+     */
+    private String bizType;
+
+    /**
+     * 变动数值(正数)
+     */
+    private Integer amount;
+
+    /**
+     * 变动后积分
+     */
+    private Integer pointsAfter;
+
+    /**
+     * 变动原因
+     */
+    private String reason;
+
+    /**
+     * 操作人ID
+     */
+    private Long operatorId;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+}

+ 70 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/FlfRewardLog.java

@@ -0,0 +1,70 @@
+package org.dromara.fulfiller.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 奖惩记录对象 flf_reward_log
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@TableName("flf_reward_log")
+public class FlfRewardLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 履约者ID
+     */
+    private Long fulfillerId;
+
+    /**
+     * 操作类型 (reward:奖励, punish:惩罚)
+     */
+    private String type;
+
+    /**
+     * 关联项目 (points:积分, balance:余额)
+     */
+    private String target;
+
+    /**
+     * 涉及数值
+     */
+    private Integer amount;
+
+    /**
+     * 奖惩原因
+     */
+    private String reason;
+
+    /**
+     * 操作人ID
+     */
+    private Long operatorId;
+
+    /**
+     * 操作人名称
+     */
+    private String operatorName;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+}

+ 68 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/SysTag.java

@@ -0,0 +1,68 @@
+package org.dromara.fulfiller.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 标签定义对象 sys_tag
+ * 复用archieves模块同表,fulfiller模块内独立Mapper
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName(value = "sys_tag", autoResultMap = true)
+@InterceptorIgnore(tenantLine = "true")
+public class SysTag extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 标签名称
+     */
+    private String name;
+
+    /**
+     * 分类 (user, pet, fulfiller)
+     */
+    private String category;
+
+    /**
+     * 颜色样式 (primary, success, warning, danger, info)
+     */
+    private String colorType;
+
+    /**
+     * 标签说明
+     */
+    private String description;
+
+    /**
+     * 类型 (1:系统内置, 2:自定义)
+     */
+    private Integer type;
+
+    /**
+     * 状态 (0:启用, 1:停用)
+     */
+    private Integer status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 45 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/SysTagRel.java

@@ -0,0 +1,45 @@
+package org.dromara.fulfiller.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 标签关联对象 sys_tag_rel
+ * 复用archieves模块同表,fulfiller模块内独立Mapper
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@TableName(value = "sys_tag_rel", autoResultMap = true)
+@InterceptorIgnore(tenantLine = "true")
+public class SysTagRel implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 标签ID
+     */
+    private Long tagId;
+
+    /**
+     * 目标对象ID
+     */
+    private Long targetId;
+
+    /**
+     * 目标类型 (user, pet, fulfiller)
+     */
+    private String targetType;
+
+}

+ 40 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfAdjustBalanceBo.java

@@ -0,0 +1,40 @@
+package org.dromara.fulfiller.domain.bo;
+
+import jakarta.validation.constraints.*;
+import lombok.Data;
+
+/**
+ * 余额调整请求体
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+public class FlfAdjustBalanceBo {
+
+    /**
+     * 履约者ID
+     */
+    @NotNull(message = "履约者ID不能为空")
+    private Long fulfillerId;
+
+    /**
+     * 调整方式 (add:增加, reduce:减少)
+     */
+    @NotBlank(message = "调整方式不能为空")
+    private String type;
+
+    /**
+     * 调整金额(分,正数)
+     */
+    @NotNull(message = "金额不能为空")
+    @Min(value = 1, message = "金额必须大于0")
+    private Long amount;
+
+    /**
+     * 调整原因
+     */
+    @NotBlank(message = "原因不能为空")
+    private String reason;
+
+}

+ 40 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfAdjustPointsBo.java

@@ -0,0 +1,40 @@
+package org.dromara.fulfiller.domain.bo;
+
+import jakarta.validation.constraints.*;
+import lombok.Data;
+
+/**
+ * 积分调整请求体
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+public class FlfAdjustPointsBo {
+
+    /**
+     * 履约者ID
+     */
+    @NotNull(message = "履约者ID不能为空")
+    private Long fulfillerId;
+
+    /**
+     * 调整方式 (add:增加, reduce:扣除)
+     */
+    @NotBlank(message = "调整方式不能为空")
+    private String type;
+
+    /**
+     * 调整数值(正数)
+     */
+    @NotNull(message = "数值不能为空")
+    @Min(value = 1, message = "数值必须大于0")
+    private Integer amount;
+
+    /**
+     * 调整原因
+     */
+    @NotBlank(message = "原因不能为空")
+    private String reason;
+
+}

+ 129 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfAuditBo.java

@@ -0,0 +1,129 @@
+package org.dromara.fulfiller.domain.bo;
+
+import org.dromara.fulfiller.domain.FlfAudit;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.util.Date;
+
+/**
+ * 履约者审核记录业务对象 flf_audit
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = FlfAudit.class, reverseConvertGenerate = false)
+public class FlfAuditBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 审核类型 (register:入驻, qualification:资质变更)
+     */
+    @NotBlank(message = "审核类型不能为空", groups = { EditGroup.class })
+    private String type;
+
+    /**
+     * 申请人姓名
+     */
+    @NotBlank(message = "姓名不能为空", groups = { AddGroup.class })
+    private String name;
+
+    /**
+     * 申请人手机号
+     */
+    @NotBlank(message = "手机号不能为空", groups = { AddGroup.class })
+    private String phone;
+
+    /**
+     * 性别
+     */
+    private String gender;
+
+    /**
+     * 出生日期
+     */
+    private Date birthday;
+
+    /**
+     * 工作类型
+     */
+    private String workType;
+
+    /**
+     * 意向城市
+     */
+    private String city;
+
+    /**
+     * 意向地点
+     */
+    private String location;
+
+    /**
+     * 证件真实姓名
+     */
+    private String realName;
+
+    /**
+     * 身份证号
+     */
+    private String idCard;
+
+    /**
+     * 证件有效期至
+     */
+    private Date idValidDate;
+
+    /**
+     * 身份证人像面(OSS ID)
+     */
+    private Long idCardFront;
+
+    /**
+     * 身份证国徽面(OSS ID)
+     */
+    private Long idCardBack;
+
+    /**
+     * 专业资质图片(JSON数组)
+     */
+    private String qualifications;
+
+    /**
+     * 申请服务类型(JSON数组)
+     */
+    private String serviceTypes;
+
+    /**
+     * 申请时设置的密码
+     */
+    private String password;
+
+    /**
+     * 意向站点ID
+     */
+    private Long stationId;
+
+    /**
+     * 状态筛选
+     */
+    private Integer status;
+
+    /**
+     * 搜索关键字(姓名/手机号)
+     */
+    private String keyword;
+
+}

+ 130 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfFulfillerBo.java

@@ -0,0 +1,130 @@
+package org.dromara.fulfiller.domain.bo;
+
+import org.dromara.fulfiller.domain.FlfFulfiller;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 履约者信息业务对象 flf_fulfiller
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = FlfFulfiller.class, reverseConvertGenerate = false)
+public class FlfFulfillerBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 姓名
+     */
+    @NotBlank(message = "姓名不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 身份证真实姓名
+     */
+    private String realName;
+
+    /**
+     * 手机号/账号
+     */
+    @NotBlank(message = "手机号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String phone;
+
+    /**
+     * 登录密码(新增时必填)
+     */
+    @NotBlank(message = "登录密码不能为空", groups = { AddGroup.class })
+    private String password;
+
+    /**
+     * 性别(0男 1女 2未知)
+     */
+    private String gender;
+
+    /**
+     * 出生日期
+     */
+    private Date birthday;
+
+    /**
+     * 身份证号
+     */
+    private String idCard;
+
+    /**
+     * 身份证有效期至
+     */
+    private Date idCardExpiry;
+
+    /**
+     * 服务类型(JSON数组)
+     */
+    private String serviceTypes;
+
+    /**
+     * 服务城市编码
+     */
+    private String cityCode;
+
+    /**
+     * 服务城市名称
+     */
+    private String cityName;
+
+    /**
+     * 归属站点ID
+     */
+    private Long stationId;
+
+    /**
+     * 工作性质
+     */
+    private String workType;
+
+    /**
+     * 等级ID
+     */
+    private Long levelId;
+
+    /**
+     * 状态
+     */
+    private String status;
+
+    /**
+     * 是否身份证认证
+     */
+    private Boolean authId;
+
+    /**
+     * 是否资质认证
+     */
+    private Boolean authQual;
+
+    /**
+     * 搜索关键字(姓名/手机号/身份证号)
+     */
+    private String keyword;
+
+    /**
+     * 标签ID列表(新增/编辑时传入)
+     */
+    private List<Long> tagIds;
+
+}

+ 46 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfRewardBo.java

@@ -0,0 +1,46 @@
+package org.dromara.fulfiller.domain.bo;
+
+import jakarta.validation.constraints.*;
+import lombok.Data;
+
+/**
+ * 奖惩操作请求体
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+public class FlfRewardBo {
+
+    /**
+     * 履约者ID
+     */
+    @NotNull(message = "履约者ID不能为空")
+    private Long fulfillerId;
+
+    /**
+     * 操作类型 (reward:奖励, punish:惩罚)
+     */
+    @NotBlank(message = "操作类型不能为空")
+    private String type;
+
+    /**
+     * 关联项目 (points:积分, balance:余额)
+     */
+    @NotBlank(message = "关联项目不能为空")
+    private String target;
+
+    /**
+     * 涉及数值(正数)
+     */
+    @NotNull(message = "数值不能为空")
+    @Min(value = 1, message = "数值必须大于0")
+    private Integer amount;
+
+    /**
+     * 奖惩原因
+     */
+    @NotBlank(message = "原因不能为空")
+    private String reason;
+
+}

+ 55 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/bo/FlfTagBo.java

@@ -0,0 +1,55 @@
+package org.dromara.fulfiller.domain.bo;
+
+import org.dromara.fulfiller.domain.SysTag;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 履约者标签业务对象
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysTag.class, reverseConvertGenerate = false)
+public class FlfTagBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 标签名称
+     */
+    @NotBlank(message = "标签名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 颜色样式
+     */
+    private String colorType;
+
+    /**
+     * 分类 (user, pet, fulfiller)
+     */
+    private String category;
+
+    /**
+     * 标签说明
+     */
+    private String description;
+
+    /**
+     * 状态
+     */
+    private Integer status;
+
+}

+ 75 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfAuditVo.java

@@ -0,0 +1,75 @@
+package org.dromara.fulfiller.domain.vo;
+
+import org.dromara.fulfiller.domain.FlfAudit;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 履约者审核记录视图对象 flf_audit
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = FlfAudit.class)
+public class FlfAuditVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private Long fulfillerId;
+    private String type;
+    private String name;
+    private String phone;
+    private String gender;
+    private Date birthday;
+    private String workType;
+    private String city;
+    private String location;
+    private String realName;
+    private String idCard;
+    private Date idValidDate;
+    private Long idCardFront;
+    private Long idCardBack;
+    private String qualifications;
+    private String serviceTypes;
+    private Long stationId;
+    private Integer status;
+    private Long auditBy;
+    private Date auditTime;
+    private String rejectReason;
+    private Date createTime;
+
+    // ===== 以下为非数据库字段,由Service层填充 =====
+
+    /** 身份证人像面图片URL */
+    @TableField(exist = false)
+    private String idCardFrontUrl;
+
+    /** 身份证国徽面图片URL */
+    @TableField(exist = false)
+    private String idCardBackUrl;
+
+    /** 专业资质图片URL列表 */
+    @TableField(exist = false)
+    private List<String> qualificationUrls;
+
+    /** 意向站点名称 */
+    @TableField(exist = false)
+    private String stationName;
+
+    /** 服务类型列表(解析后) */
+    @TableField(exist = false)
+    private List<String> serviceTypeList;
+
+}

+ 34 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfBalanceLogVo.java

@@ -0,0 +1,34 @@
+package org.dromara.fulfiller.domain.vo;
+
+import org.dromara.fulfiller.domain.FlfBalanceLog;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 余额变动记录视图对象
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@AutoMapper(target = FlfBalanceLog.class)
+public class FlfBalanceLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private Long fulfillerId;
+    private String type;
+    private String subType;
+    private Long amount;
+    private Long balanceAfter;
+    private String reason;
+    private Long operatorId;
+    private Date createTime;
+
+}

+ 79 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfFulfillerVo.java

@@ -0,0 +1,79 @@
+package org.dromara.fulfiller.domain.vo;
+
+import org.dromara.fulfiller.domain.FlfFulfiller;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 履约者信息视图对象 flf_fulfiller
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = FlfFulfiller.class)
+public class FlfFulfillerVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private Long userId;
+    private String name;
+    private String realName;
+    private String phone;
+    private String gender;
+    private Date birthday;
+    private Integer age;
+    private Long avatar;
+    private String avatarUrl;
+    private String idCard;
+    private Long idCardFront;
+    private Long idCardBack;
+    private Date idCardExpiry;
+    private String serviceTypes;
+    private String cityCode;
+    private String cityName;
+    private Long stationId;
+    private String stationName;
+    private String workType;
+    private Long levelId;
+    private String levelName;
+    private Integer points;
+    private Long balance;
+    private String status;
+    private Boolean authId;
+    private Boolean authQual;
+    private String qualImages;
+    private Integer orderCount;
+    private Integer rejectCount;
+    private BigDecimal rating;
+    private Date createTime;
+
+    /** 身份证正面图片URL */
+    private String idCardFrontUrl;
+    /** 身份证背面图片URL */
+    private String idCardBackUrl;
+    /** 资质证书图片URL列表 */
+    private List<String> qualImageUrls;
+    /** 服务类型列表(解析后) */
+    private List<String> serviceTypeList;
+    /** 是否已设置密码 */
+    private Boolean hasPassword;
+    /** 已注册天数 */
+    private Long registerDays;
+
+    /**
+     * 关联标签列表
+     */
+    private List<SysTagVo> tags;
+
+}

+ 34 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfPointsLogVo.java

@@ -0,0 +1,34 @@
+package org.dromara.fulfiller.domain.vo;
+
+import org.dromara.fulfiller.domain.FlfPointsLog;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 积分变动记录视图对象
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@AutoMapper(target = FlfPointsLog.class)
+public class FlfPointsLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private Long fulfillerId;
+    private String type;
+    private String bizType;
+    private Integer amount;
+    private Integer pointsAfter;
+    private String reason;
+    private Long operatorId;
+    private Date createTime;
+
+}

+ 34 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfRewardLogVo.java

@@ -0,0 +1,34 @@
+package org.dromara.fulfiller.domain.vo;
+
+import org.dromara.fulfiller.domain.FlfRewardLog;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 奖惩记录视图对象
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@AutoMapper(target = FlfRewardLog.class)
+public class FlfRewardLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private Long fulfillerId;
+    private String type;
+    private String target;
+    private Integer amount;
+    private String reason;
+    private Long operatorId;
+    private String operatorName;
+    private Date createTime;
+
+}

+ 35 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/SysTagVo.java

@@ -0,0 +1,35 @@
+package org.dromara.fulfiller.domain.vo;
+
+import org.dromara.fulfiller.domain.SysTag;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 标签视图对象
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysTag.class)
+public class SysTagVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private String name;
+    private String category;
+    private String colorType;
+    private String description;
+    private Integer type;
+    private Integer status;
+    private Date createTime;
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfAuditMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.FlfAudit;
+import org.dromara.fulfiller.domain.vo.FlfAuditVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 履约者审核记录Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface FlfAuditMapper extends BaseMapperPlus<FlfAudit, FlfAuditVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfBalanceLogMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.FlfBalanceLog;
+import org.dromara.fulfiller.domain.vo.FlfBalanceLogVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 余额变动记录Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface FlfBalanceLogMapper extends BaseMapperPlus<FlfBalanceLog, FlfBalanceLogVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfFulfillerMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.FlfFulfiller;
+import org.dromara.fulfiller.domain.vo.FlfFulfillerVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 履约者信息Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface FlfFulfillerMapper extends BaseMapperPlus<FlfFulfiller, FlfFulfillerVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfPointsLogMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.FlfPointsLog;
+import org.dromara.fulfiller.domain.vo.FlfPointsLogVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 积分变动记录Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface FlfPointsLogMapper extends BaseMapperPlus<FlfPointsLog, FlfPointsLogVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/FlfRewardLogMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.FlfRewardLog;
+import org.dromara.fulfiller.domain.vo.FlfRewardLogVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 奖惩记录Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface FlfRewardLogMapper extends BaseMapperPlus<FlfRewardLog, FlfRewardLogVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/SysTagMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.SysTag;
+import org.dromara.fulfiller.domain.vo.SysTagVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 标签Mapper接口(fulfiller模块独立副本)
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface SysTagMapper extends BaseMapperPlus<SysTag, SysTagVo> {
+
+}

+ 14 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/mapper/SysTagRelMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.fulfiller.mapper;
+
+import org.dromara.fulfiller.domain.SysTagRel;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 标签关联Mapper接口(fulfiller模块独立副本)
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface SysTagRelMapper extends BaseMapperPlus<SysTagRel, SysTagRel> {
+
+}

+ 46 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfAuditService.java

@@ -0,0 +1,46 @@
+package org.dromara.fulfiller.service;
+
+import org.dromara.fulfiller.domain.bo.FlfAuditBo;
+import org.dromara.fulfiller.domain.vo.FlfAuditVo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+/**
+ * 履约者审核记录Service接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface IFlfAuditService {
+
+    /**
+     * 查询审核记录详情
+     */
+    FlfAuditVo queryById(Long id);
+
+    /**
+     * 查询审核记录分页列表
+     */
+    TableDataInfo<FlfAuditVo> queryPageList(FlfAuditBo bo, PageQuery pageQuery);
+
+    /**
+     * 提交入驻申请(App端)
+     */
+    Boolean submitApply(FlfAuditBo bo);
+
+    /**
+     * 查询待审核数量
+     */
+    Long countPending();
+
+    /**
+     * 审核通过
+     */
+    Boolean pass(Long id);
+
+    /**
+     * 审核驳回
+     */
+    Boolean reject(Long id, String rejectReason);
+
+}

+ 123 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfFulfillerService.java

@@ -0,0 +1,123 @@
+package org.dromara.fulfiller.service;
+
+import org.dromara.fulfiller.domain.bo.FlfFulfillerBo;
+import org.dromara.fulfiller.domain.bo.FlfAdjustBalanceBo;
+import org.dromara.fulfiller.domain.bo.FlfAdjustPointsBo;
+import org.dromara.fulfiller.domain.bo.FlfRewardBo;
+import org.dromara.fulfiller.domain.vo.FlfFulfillerVo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.List;
+
+/**
+ * 履约者信息Service接口
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface IFlfFulfillerService {
+
+    /**
+     * 查询履约者详情
+     */
+    FlfFulfillerVo queryById(Long id);
+
+    /**
+     * 查询履约者分页列表
+     */
+    TableDataInfo<FlfFulfillerVo> queryPageList(FlfFulfillerBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询履约者列表(不分页)
+     */
+    List<FlfFulfillerVo> queryList(FlfFulfillerBo bo);
+
+    /**
+     * 新增履约者
+     */
+    Boolean insertByBo(FlfFulfillerBo bo);
+
+    /**
+     * 修改履约者
+     */
+    Boolean updateByBo(FlfFulfillerBo bo);
+
+    /**
+     * 切换状态
+     */
+    Boolean changeStatus(Long id, String status);
+
+    /**
+     * 重置密码
+     */
+    Boolean resetPwd(Long id, String password);
+
+    /**
+     * 奖惩操作
+     */
+    Boolean reward(FlfRewardBo bo);
+
+    /**
+     * 调整积分
+     */
+    Boolean adjustPoints(FlfAdjustPointsBo bo);
+
+    /**
+     * 调整余额
+     */
+    Boolean adjustBalance(FlfAdjustBalanceBo bo);
+
+    /**
+     * 根据手机号查询履约者
+     */
+    FlfFulfillerVo queryByPhone(String phone);
+
+    /**
+     * 根据系统用户ID查询履约者
+     */
+    FlfFulfillerVo queryByUserId(Long userId);
+
+    /**
+     * 修改头像(App端)
+     * @author steelwei
+     */
+    Boolean updateAvatarByUserId(Long userId, String avatar);
+
+    /**
+     * 修改真实姓名(App端)
+     * @author steelwei
+     */
+    Boolean updateNameByUserId(Long userId, String name);
+
+    /**
+     * 修改工作状态(App端)
+     * @author steelwei
+     */
+    Boolean updateStatusByUserId(Long userId, String status);
+
+    /**
+     * 修改工作城市(App端)
+     * @author steelwei
+     */
+    Boolean updateCityByUserId(Long userId, String cityCode, String cityName);
+
+    /**
+     * 修改手机号(App端)
+     * @author steelwei
+     */
+    Boolean updatePhoneByUserId(Long userId, String phone);
+
+    /**
+     * 修改密码(App端)
+     * @author steelwei
+     */
+    Boolean updatePasswordByUserId(Long userId, String oldPassword, String newPassword);
+
+    /**
+     * 注销账号(App端)
+     * @author steelwei
+     */
+    Boolean deleteAccountByUserId(Long userId);
+
+}

+ 49 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfTagService.java

@@ -0,0 +1,49 @@
+package org.dromara.fulfiller.service;
+
+import org.dromara.fulfiller.domain.bo.FlfTagBo;
+import org.dromara.fulfiller.domain.vo.SysTagVo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 标签管理Service接口(fulfiller模块)
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+public interface IFlfTagService {
+
+    /**
+     * 查询标签详情
+     */
+    SysTagVo queryById(Long id);
+
+    /**
+     * 查询标签分页列表
+     */
+    TableDataInfo<SysTagVo> queryPageList(FlfTagBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询标签列表(不分页)
+     */
+    List<SysTagVo> queryList(FlfTagBo bo);
+
+    /**
+     * 新增标签
+     */
+    Boolean insertByBo(FlfTagBo bo);
+
+    /**
+     * 修改标签
+     */
+    Boolean updateByBo(FlfTagBo bo);
+
+    /**
+     * 批量删除标签
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+}

+ 247 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfAuditServiceImpl.java

@@ -0,0 +1,247 @@
+package org.dromara.fulfiller.service.impl;
+
+import org.dromara.common.core.enums.UserType;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.platform.Platform;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.dromara.fulfiller.domain.FlfAudit;
+import org.dromara.fulfiller.domain.FlfFulfiller;
+import org.dromara.fulfiller.domain.bo.FlfAuditBo;
+import org.dromara.fulfiller.domain.vo.FlfAuditVo;
+import org.dromara.fulfiller.mapper.FlfAuditMapper;
+import org.dromara.fulfiller.mapper.FlfFulfillerMapper;
+import org.dromara.fulfiller.service.IFlfAuditService;
+import org.dromara.resource.api.RemoteFileService;
+import org.dromara.resource.api.domain.RemoteFile;
+import org.dromara.system.api.RemoteAreaStationService;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.bo.RemoteUserBo;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 履约者审核记录Service业务层处理
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class FlfAuditServiceImpl implements IFlfAuditService {
+
+    private final FlfAuditMapper baseMapper;
+    private final FlfFulfillerMapper fulfillerMapper;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @DubboReference
+    private RemoteFileService remoteFileService;
+
+    @DubboReference
+    private RemoteAreaStationService remoteAreaStationService;
+
+    @Override
+    public FlfAuditVo queryById(Long id) {
+        FlfAuditVo vo = baseMapper.selectVoById(id);
+        if (vo != null) {
+            enrichVo(vo);
+        }
+        return vo;
+    }
+
+    @Override
+    public TableDataInfo<FlfAuditVo> queryPageList(FlfAuditBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<FlfAudit> lqw = buildQueryWrapper(bo);
+        Page<FlfAuditVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        result.getRecords().forEach(this::enrichVo);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 填充VO的图片URL、站点名称、服务类型列表
+     */
+    private void enrichVo(FlfAuditVo vo) {
+        try {
+            // 解析服务类型 JSON
+            if (StringUtils.isNotBlank(vo.getServiceTypes())) {
+                vo.setServiceTypeList(com.alibaba.fastjson2.JSON.parseArray(vo.getServiceTypes(), String.class));
+            }
+            // 解析站点名称
+            if (vo.getStationId() != null) {
+                vo.setStationName(remoteAreaStationService.selectNameById(vo.getStationId()));
+            }
+            // 解析身份证图片URL
+            List<String> ossIds = new ArrayList<>();
+            if (vo.getIdCardFront() != null) ossIds.add(String.valueOf(vo.getIdCardFront()));
+            if (vo.getIdCardBack() != null) ossIds.add(String.valueOf(vo.getIdCardBack()));
+            // 解析资质图片 OSS IDs
+            List<String> qualOssIds = new ArrayList<>();
+            if (StringUtils.isNotBlank(vo.getQualifications())) {
+                qualOssIds = com.alibaba.fastjson2.JSON.parseArray(vo.getQualifications(), String.class);
+                ossIds.addAll(qualOssIds);
+            }
+            // 批量查询 OSS URL
+            if (!ossIds.isEmpty()) {
+                String idsStr = String.join(",", ossIds);
+                List<RemoteFile> files = remoteFileService.selectByIds(idsStr);
+                for (RemoteFile f : files) {
+                    if (vo.getIdCardFront() != null && f.getOssId().equals(vo.getIdCardFront())) {
+                        vo.setIdCardFrontUrl(f.getUrl());
+                    }
+                    if (vo.getIdCardBack() != null && f.getOssId().equals(vo.getIdCardBack())) {
+                        vo.setIdCardBackUrl(f.getUrl());
+                    }
+                }
+                // 资质图片URL
+                List<String> qualUrls = new ArrayList<>();
+                for (String qId : qualOssIds) {
+                    for (RemoteFile f : files) {
+                        if (String.valueOf(f.getOssId()).equals(qId)) {
+                            qualUrls.add(f.getUrl());
+                            break;
+                        }
+                    }
+                }
+                vo.setQualificationUrls(qualUrls);
+            }
+        } catch (Exception e) {
+            log.warn("填充审核VO扩展字段失败: id={}", vo.getId(), e);
+        }
+    }
+
+    @Override
+    public Long countPending() {
+        return baseMapper.selectCount(Wrappers.<FlfAudit>lambdaQuery()
+            .eq(FlfAudit::getStatus, 0));
+    }
+
+    private LambdaQueryWrapper<FlfAudit> buildQueryWrapper(FlfAuditBo bo) {
+        LambdaQueryWrapper<FlfAudit> lqw = Wrappers.lambdaQuery();
+        lqw.and(StringUtils.isNotBlank(bo.getKeyword()), w ->
+            w.like(FlfAudit::getName, bo.getKeyword())
+                .or().like(FlfAudit::getPhone, bo.getKeyword())
+        );
+        lqw.eq(StringUtils.isNotBlank(bo.getType()), FlfAudit::getType, bo.getType());
+        lqw.eq(bo.getStatus() != null, FlfAudit::getStatus, bo.getStatus());
+        lqw.orderByDesc(FlfAudit::getCreateTime);
+        return lqw;
+    }
+
+    @Override
+    public Boolean submitApply(FlfAuditBo bo) {
+        FlfAudit add = MapstructUtils.convert(bo, FlfAudit.class);
+        add.setStatus(0);
+        // 密码BCrypt加密后存储,避免明文存入数据库
+        if (StringUtils.isNotBlank(add.getPassword())) {
+            add.setPassword(cn.hutool.crypto.digest.BCrypt.hashpw(add.getPassword()));
+        }
+        return baseMapper.insert(add) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean pass(Long id) {
+        FlfAudit audit = baseMapper.selectById(id);
+        if (audit == null) {
+            throw new RuntimeException("审核记录不存在");
+        }
+        if (audit.getStatus() != 0) {
+            throw new RuntimeException("该记录已处理,不可重复操作");
+        }
+
+        // 更新审核状态
+        FlfAudit update = new FlfAudit();
+        update.setId(id);
+        update.setStatus(1);
+        update.setAuditBy(LoginHelper.getUserId());
+        update.setAuditTime(new Date());
+        baseMapper.updateById(update);
+
+        // 创建或更新履约者信息
+        if ("register".equals(audit.getType())) {
+            // 1. 创建 sys_user 记录(platformId=FULFILLER, userType=fulfiller_user)
+            RemoteUserBo userBo = new RemoteUserBo();
+            userBo.setUserName(audit.getPhone());
+            userBo.setNickName(audit.getName());
+            // 密码已在submitApply时BCrypt加密,直接使用
+            userBo.setPassword(audit.getPassword());
+            userBo.setPhonenumber(audit.getPhone());
+            userBo.setSex(audit.getGender());
+            userBo.setUserType(UserType.FULFILLER_USER.getUserType());
+            userBo.setPlatformId(Platform.FULFILLER.getId());
+            userBo.setStatus("0");
+            userBo.setTenantId(TenantHelper.getTenantId());
+            Long sysUserId = remoteUserService.createUser(userBo);
+
+            // 2. 创建 flf_fulfiller 记录
+            FlfFulfiller fulfiller = new FlfFulfiller();
+            fulfiller.setUserId(sysUserId);
+            fulfiller.setName(audit.getName());
+            fulfiller.setRealName(audit.getRealName());
+            fulfiller.setPhone(audit.getPhone());
+            fulfiller.setPassword(audit.getPassword()); // 已BCrypt加密
+            fulfiller.setGender(audit.getGender());
+            fulfiller.setBirthday(audit.getBirthday());
+            fulfiller.setIdCard(audit.getIdCard());
+            fulfiller.setIdCardFront(audit.getIdCardFront());
+            fulfiller.setIdCardBack(audit.getIdCardBack());
+            fulfiller.setIdCardExpiry(audit.getIdValidDate());
+            fulfiller.setServiceTypes(audit.getServiceTypes());
+            fulfiller.setWorkType(audit.getWorkType());
+            fulfiller.setStationId(audit.getStationId());
+            fulfiller.setQualImages(audit.getQualifications());
+            fulfiller.setStatus("resting");
+            fulfiller.setAuthId(true);
+            fulfiller.setAuthQual(StringUtils.isNotBlank(audit.getQualifications()));
+            fulfiller.setPoints(0);
+            fulfiller.setBalance(0L);
+            fulfiller.setOrderCount(0);
+            fulfiller.setRejectCount(0);
+            fulfillerMapper.insert(fulfiller);
+
+            // 3. 回写fulfillerId到审核记录
+            FlfAudit auditUpdate2 = new FlfAudit();
+            auditUpdate2.setId(id);
+            auditUpdate2.setFulfillerId(fulfiller.getId());
+            baseMapper.updateById(auditUpdate2);
+        }
+
+        return true;
+    }
+
+    @Override
+    public Boolean reject(Long id, String rejectReason) {
+        FlfAudit audit = baseMapper.selectById(id);
+        if (audit == null) {
+            throw new RuntimeException("审核记录不存在");
+        }
+        if (audit.getStatus() != 0) {
+            throw new RuntimeException("该记录已处理,不可重复操作");
+        }
+
+        FlfAudit update = new FlfAudit();
+        update.setId(id);
+        update.setStatus(2);
+        update.setRejectReason(rejectReason);
+        update.setAuditBy(LoginHelper.getUserId());
+        update.setAuditTime(new Date());
+        return baseMapper.updateById(update) > 0;
+    }
+
+}

+ 524 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfFulfillerServiceImpl.java

@@ -0,0 +1,524 @@
+package org.dromara.fulfiller.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import cn.hutool.crypto.digest.BCrypt;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.system.api.RemoteUserService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.dromara.fulfiller.domain.FlfFulfiller;
+import org.dromara.fulfiller.domain.FlfPointsLog;
+import org.dromara.fulfiller.domain.FlfBalanceLog;
+import org.dromara.fulfiller.domain.FlfRewardLog;
+import org.dromara.fulfiller.domain.SysTag;
+import org.dromara.fulfiller.domain.SysTagRel;
+import org.dromara.fulfiller.domain.bo.FlfFulfillerBo;
+import org.dromara.fulfiller.domain.bo.FlfAdjustBalanceBo;
+import org.dromara.fulfiller.domain.bo.FlfAdjustPointsBo;
+import org.dromara.fulfiller.domain.bo.FlfRewardBo;
+import org.dromara.fulfiller.domain.vo.FlfFulfillerVo;
+import org.dromara.fulfiller.domain.vo.SysTagVo;
+import org.dromara.fulfiller.mapper.FlfFulfillerMapper;
+import org.dromara.fulfiller.mapper.FlfPointsLogMapper;
+import org.dromara.fulfiller.mapper.FlfBalanceLogMapper;
+import org.dromara.fulfiller.mapper.FlfRewardLogMapper;
+import org.dromara.fulfiller.mapper.SysTagMapper;
+import org.dromara.fulfiller.mapper.SysTagRelMapper;
+import org.dromara.fulfiller.service.IFlfFulfillerService;
+
+import org.dromara.resource.api.RemoteFileService;
+import org.dromara.resource.api.domain.RemoteFile;
+import org.dromara.system.api.RemoteAreaStationService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 履约者信息Service业务层处理
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class FlfFulfillerServiceImpl implements IFlfFulfillerService {
+
+    private final FlfFulfillerMapper baseMapper;
+    private final FlfPointsLogMapper pointsLogMapper;
+    private final FlfBalanceLogMapper balanceLogMapper;
+    private final FlfRewardLogMapper rewardLogMapper;
+    private final SysTagMapper tagMapper;
+    private final SysTagRelMapper tagRelMapper;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @DubboReference
+    private RemoteFileService remoteFileService;
+
+    @DubboReference
+    private RemoteAreaStationService remoteAreaStationService;
+
+    @Override
+    public FlfFulfillerVo queryById(Long id) {
+        FlfFulfillerVo vo = baseMapper.selectVoById(id);
+        if (vo != null) {
+            vo.setTags(queryTagsByTargetId(id, "fulfiller"));
+            enrichVo(vo);
+        }
+        return vo;
+    }
+
+    @Override
+    public TableDataInfo<FlfFulfillerVo> queryPageList(FlfFulfillerBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<FlfFulfiller> lqw = buildQueryWrapper(bo);
+        Page<FlfFulfillerVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        result.getRecords().forEach(vo -> {
+            vo.setTags(queryTagsByTargetId(vo.getId(), "fulfiller"));
+            enrichVo(vo);
+        });
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<FlfFulfillerVo> queryList(FlfFulfillerBo bo) {
+        LambdaQueryWrapper<FlfFulfiller> lqw = buildQueryWrapper(bo);
+        List<FlfFulfillerVo> list = baseMapper.selectVoList(lqw);
+        list.forEach(vo -> vo.setTags(queryTagsByTargetId(vo.getId(), "fulfiller")));
+        return list;
+    }
+
+    private LambdaQueryWrapper<FlfFulfiller> buildQueryWrapper(FlfFulfillerBo bo) {
+        LambdaQueryWrapper<FlfFulfiller> lqw = Wrappers.lambdaQuery();
+        lqw.and(StringUtils.isNotBlank(bo.getKeyword()), w ->
+            w.like(FlfFulfiller::getName, bo.getKeyword())
+                .or().like(FlfFulfiller::getPhone, bo.getKeyword())
+                .or().like(FlfFulfiller::getIdCard, bo.getKeyword())
+        );
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), FlfFulfiller::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getWorkType()), FlfFulfiller::getWorkType, bo.getWorkType());
+        lqw.eq(StringUtils.isNotBlank(bo.getCityCode()), FlfFulfiller::getCityCode, bo.getCityCode());
+        lqw.eq(bo.getStationId() != null, FlfFulfiller::getStationId, bo.getStationId());
+        lqw.eq(bo.getLevelId() != null, FlfFulfiller::getLevelId, bo.getLevelId());
+        lqw.eq(bo.getAuthId() != null, FlfFulfiller::getAuthId, bo.getAuthId());
+        lqw.eq(bo.getAuthQual() != null, FlfFulfiller::getAuthQual, bo.getAuthQual());
+        lqw.orderByDesc(FlfFulfiller::getCreateTime);
+        return lqw;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean insertByBo(FlfFulfillerBo bo) {
+        FlfFulfiller add = MapstructUtils.convert(bo, FlfFulfiller.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+            saveTagRels(add.getId(), bo.getTagIds(), "fulfiller");
+        }
+        return flag;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateByBo(FlfFulfillerBo bo) {
+        FlfFulfiller update = MapstructUtils.convert(bo, FlfFulfiller.class);
+        validEntityBeforeSave(update);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag) {
+            saveTagRels(bo.getId(), bo.getTagIds(), "fulfiller");
+        }
+        return flag;
+    }
+
+    @Override
+    public Boolean changeStatus(Long id, String status) {
+        FlfFulfiller update = new FlfFulfiller();
+        update.setId(id);
+        update.setStatus(status);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    @Override
+    public Boolean resetPwd(Long id, String password) {
+        FlfFulfiller fulfiller = baseMapper.selectById(id);
+        if (fulfiller == null) {
+            throw new RuntimeException("履约者不存在");
+        }
+        String encryptedPwd = BCrypt.hashpw(password);
+        // 更新 flf_fulfiller 表密码
+        FlfFulfiller update = new FlfFulfiller();
+        update.setId(id);
+        update.setPassword(encryptedPwd);
+        baseMapper.updateById(update);
+        // 同步更新 sys_user 表密码
+        if (fulfiller.getUserId() != null) {
+            remoteUserService.resetUserPwd(fulfiller.getUserId(), encryptedPwd);
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean reward(FlfRewardBo bo) {
+        FlfFulfiller fulfiller = baseMapper.selectById(bo.getFulfillerId());
+        if (fulfiller == null) {
+            throw new RuntimeException("履约者不存在");
+        }
+
+        int delta = "punish".equals(bo.getType()) ? -bo.getAmount() : bo.getAmount();
+
+        if ("points".equals(bo.getTarget())) {
+            int newPoints = fulfiller.getPoints() + delta;
+            FlfFulfiller update = new FlfFulfiller();
+            update.setId(fulfiller.getId());
+            update.setPoints(newPoints);
+            baseMapper.updateById(update);
+
+            // 记录积分日志
+            FlfPointsLog plog = new FlfPointsLog();
+            plog.setFulfillerId(fulfiller.getId());
+            plog.setType(bo.getType());
+            plog.setBizType("admin_" + bo.getType());
+            plog.setAmount(delta);
+            plog.setPointsAfter(newPoints);
+            plog.setReason(bo.getReason());
+            plog.setOperatorId(LoginHelper.getUserId());
+            pointsLogMapper.insert(plog);
+        } else if ("balance".equals(bo.getTarget())) {
+            long longDelta = (long) delta;
+            long newBalance = fulfiller.getBalance() + longDelta;
+            FlfFulfiller update = new FlfFulfiller();
+            update.setId(fulfiller.getId());
+            update.setBalance(newBalance);
+            baseMapper.updateById(update);
+
+            // 记录余额日志
+            FlfBalanceLog blog = new FlfBalanceLog();
+            blog.setFulfillerId(fulfiller.getId());
+            blog.setType(bo.getType());
+            blog.setSubType("admin_" + bo.getType());
+            blog.setAmount(longDelta);
+            blog.setBalanceAfter(newBalance);
+            blog.setReason(bo.getReason());
+            blog.setOperatorId(LoginHelper.getUserId());
+            balanceLogMapper.insert(blog);
+        }
+
+        // 记录奖惩日志
+        FlfRewardLog rlog = new FlfRewardLog();
+        rlog.setFulfillerId(fulfiller.getId());
+        rlog.setType(bo.getType());
+        rlog.setTarget(bo.getTarget());
+        rlog.setAmount(bo.getAmount());
+        rlog.setReason(bo.getReason());
+        rlog.setOperatorId(LoginHelper.getUserId());
+        rewardLogMapper.insert(rlog);
+
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean adjustPoints(FlfAdjustPointsBo bo) {
+        FlfFulfiller fulfiller = baseMapper.selectById(bo.getFulfillerId());
+        if (fulfiller == null) {
+            throw new RuntimeException("履约者不存在");
+        }
+
+        int newPoints = fulfiller.getPoints() + bo.getAmount();
+        FlfFulfiller update = new FlfFulfiller();
+        update.setId(fulfiller.getId());
+        update.setPoints(newPoints);
+        baseMapper.updateById(update);
+
+        FlfPointsLog plog = new FlfPointsLog();
+        plog.setFulfillerId(fulfiller.getId());
+        plog.setType(bo.getAmount() > 0 ? "increase" : "decrease");
+        plog.setBizType("admin_adjust");
+        plog.setAmount(bo.getAmount());
+        plog.setPointsAfter(newPoints);
+        plog.setReason(bo.getReason());
+        plog.setOperatorId(LoginHelper.getUserId());
+        pointsLogMapper.insert(plog);
+
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean adjustBalance(FlfAdjustBalanceBo bo) {
+        FlfFulfiller fulfiller = baseMapper.selectById(bo.getFulfillerId());
+        if (fulfiller == null) {
+            throw new RuntimeException("履约者不存在");
+        }
+
+        long newBalance = fulfiller.getBalance() + bo.getAmount();
+        FlfFulfiller update = new FlfFulfiller();
+        update.setId(fulfiller.getId());
+        update.setBalance(newBalance);
+        baseMapper.updateById(update);
+
+        FlfBalanceLog blog = new FlfBalanceLog();
+        blog.setFulfillerId(fulfiller.getId());
+        blog.setType(bo.getAmount() > 0 ? "increase" : "decrease");
+        blog.setSubType("admin_adjust");
+        blog.setAmount(bo.getAmount());
+        blog.setBalanceAfter(newBalance);
+        blog.setReason(bo.getReason());
+        blog.setOperatorId(LoginHelper.getUserId());
+        balanceLogMapper.insert(blog);
+
+        return true;
+    }
+
+    @Override
+    public FlfFulfillerVo queryByPhone(String phone) {
+        FlfFulfillerVo vo = baseMapper.selectVoOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getPhone, phone)
+        );
+        if (vo != null) {
+            vo.setTags(queryTagsByTargetId(vo.getId(), "fulfiller"));
+        }
+        return vo;
+    }
+
+    @Override
+    public FlfFulfillerVo queryByUserId(Long userId) {
+        FlfFulfillerVo vo = baseMapper.selectVoOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (vo != null) {
+            vo.setTags(queryTagsByTargetId(vo.getId(), "fulfiller"));
+            enrichVo(vo);
+        }
+        return vo;
+    }
+
+    /**
+     * 填充VO的图片URL、站点名称、服务类型列表等
+     * @author steelwei
+     */
+    private void enrichVo(FlfFulfillerVo vo) {
+        try {
+            // 解析站点名称
+            if (vo.getStationId() != null) {
+                vo.setStationName(remoteAreaStationService.selectNameById(vo.getStationId()));
+            }
+            // 解析服务类型 JSON
+            if (StringUtils.isNotBlank(vo.getServiceTypes())) {
+                vo.setServiceTypeList(com.alibaba.fastjson2.JSON.parseArray(vo.getServiceTypes(), String.class));
+            }
+            // 计算注册天数
+            if (vo.getCreateTime() != null) {
+                long days = java.time.temporal.ChronoUnit.DAYS.between(
+                    vo.getCreateTime().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(),
+                    java.time.LocalDate.now()
+                );
+                vo.setRegisterDays(Math.max(days, 0));
+            } else {
+                vo.setRegisterDays(0L);
+            }
+            // 是否已设置密码(查数据库原始记录判断)
+            FlfFulfiller entity = baseMapper.selectById(vo.getId());
+            vo.setHasPassword(entity != null && StringUtils.isNotBlank(entity.getPassword()));
+
+            // 收集所有需要查询URL的OSS ID
+            List<String> ossIds = new ArrayList<>();
+            if (vo.getAvatar() != null) ossIds.add(String.valueOf(vo.getAvatar()));
+            if (vo.getIdCardFront() != null) ossIds.add(String.valueOf(vo.getIdCardFront()));
+            if (vo.getIdCardBack() != null) ossIds.add(String.valueOf(vo.getIdCardBack()));
+            // 解析资质图片 OSS IDs
+            List<String> qualOssIds = new ArrayList<>();
+            if (StringUtils.isNotBlank(vo.getQualImages())) {
+                qualOssIds = com.alibaba.fastjson2.JSON.parseArray(vo.getQualImages(), String.class);
+                ossIds.addAll(qualOssIds);
+            }
+            // 批量查询 OSS URL
+            if (!ossIds.isEmpty()) {
+                String idsStr = String.join(",", ossIds);
+                List<RemoteFile> files = remoteFileService.selectByIds(idsStr);
+                for (RemoteFile f : files) {
+                    if (vo.getAvatar() != null && f.getOssId().equals(vo.getAvatar())) {
+                        vo.setAvatarUrl(f.getUrl());
+                    }
+                    if (vo.getIdCardFront() != null && f.getOssId().equals(vo.getIdCardFront())) {
+                        vo.setIdCardFrontUrl(f.getUrl());
+                    }
+                    if (vo.getIdCardBack() != null && f.getOssId().equals(vo.getIdCardBack())) {
+                        vo.setIdCardBackUrl(f.getUrl());
+                    }
+                }
+                // 资质图片URL
+                List<String> qualUrls = new ArrayList<>();
+                for (String qId : qualOssIds) {
+                    for (RemoteFile f : files) {
+                        if (String.valueOf(f.getOssId()).equals(qId)) {
+                            qualUrls.add(f.getUrl());
+                            break;
+                        }
+                    }
+                }
+                vo.setQualImageUrls(qualUrls);
+            }
+        } catch (Exception e) {
+            log.warn("enrichVo失败: {}", e.getMessage());
+        }
+    }
+
+    private void validEntityBeforeSave(FlfFulfiller entity) {
+        //TODO 做一些数据校验,如手机号唯一约束
+    }
+
+    /**
+     * 查询目标关联的标签列表
+     */
+    private List<SysTagVo> queryTagsByTargetId(Long targetId, String targetType) {
+        List<SysTagRel> rels = tagRelMapper.selectList(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, targetId)
+                .eq(SysTagRel::getTargetType, targetType)
+        );
+        if (rels.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<Long> tagIds = rels.stream().map(SysTagRel::getTagId).collect(Collectors.toList());
+        return tagMapper.selectVoList(
+            Wrappers.lambdaQuery(SysTag.class).in(SysTag::getId, tagIds)
+        );
+    }
+
+    /**
+     * 保存标签关联(先删后增)
+     */
+    private void saveTagRels(Long targetId, List<Long> tagIds, String targetType) {
+        tagRelMapper.delete(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, targetId)
+                .eq(SysTagRel::getTargetType, targetType)
+        );
+        if (tagIds != null && !tagIds.isEmpty()) {
+            tagIds.forEach(tagId -> {
+                SysTagRel rel = new SysTagRel();
+                rel.setTagId(tagId);
+                rel.setTargetId(targetId);
+                rel.setTargetType(targetType);
+                tagRelMapper.insert(rel);
+            });
+        }
+    }
+
+    @Override
+    public Boolean updateAvatarByUserId(Long userId, String avatar) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        fulfiller.setAvatar(avatar);
+        return baseMapper.updateById(fulfiller) > 0;
+    }
+
+    @Override
+    public Boolean updateNameByUserId(Long userId, String name) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        fulfiller.setRealName(name);
+        return baseMapper.updateById(fulfiller) > 0;
+    }
+
+    @Override
+    public Boolean updateStatusByUserId(Long userId, String status) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        fulfiller.setStatus(status);
+        return baseMapper.updateById(fulfiller) > 0;
+    }
+
+    @Override
+    public Boolean updateCityByUserId(Long userId, String cityCode, String cityName) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        fulfiller.setCityCode(cityCode);
+        fulfiller.setCityName(cityName);
+        return baseMapper.updateById(fulfiller) > 0;
+    }
+
+    @Override
+    public Boolean updatePhoneByUserId(Long userId, String phone) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        // 检查手机号是否已被使用
+        Long count = baseMapper.selectCount(
+            Wrappers.lambdaQuery(FlfFulfiller.class)
+                .eq(FlfFulfiller::getPhone, phone)
+                .ne(FlfFulfiller::getId, fulfiller.getId())
+        );
+        if (count > 0) {
+            throw new RuntimeException("该手机号已被使用");
+        }
+        fulfiller.setPhone(phone);
+        return baseMapper.updateById(fulfiller) > 0;
+    }
+
+    @Override
+    public Boolean updatePasswordByUserId(Long userId, String oldPassword, String newPassword) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        // 验证旧密码
+        if (!BCrypt.checkpw(oldPassword, fulfiller.getPassword())) {
+            throw new RuntimeException("原密码错误");
+        }
+        // 加密新密码
+        String hashedPassword = BCrypt.hashpw(newPassword);
+        fulfiller.setPassword(hashedPassword);
+        return baseMapper.updateById(fulfiller) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteAccountByUserId(Long userId) {
+        FlfFulfiller fulfiller = baseMapper.selectOne(
+            Wrappers.lambdaQuery(FlfFulfiller.class).eq(FlfFulfiller::getUserId, userId)
+        );
+        if (fulfiller == null) {
+            return false;
+        }
+        // 逻辑删除履约者信息
+        return baseMapper.deleteById(fulfiller.getId()) > 0;
+    }
+
+}

+ 92 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfTagServiceImpl.java

@@ -0,0 +1,92 @@
+package org.dromara.fulfiller.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.fulfiller.domain.SysTag;
+import org.dromara.fulfiller.domain.bo.FlfTagBo;
+import org.dromara.fulfiller.domain.vo.SysTagVo;
+import org.dromara.fulfiller.mapper.SysTagMapper;
+import org.dromara.fulfiller.service.IFlfTagService;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 标签管理Service业务层处理(fulfiller模块)
+ *
+ * @author steelwei
+ * @date 2026-03-01
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class FlfTagServiceImpl implements IFlfTagService {
+
+    private final SysTagMapper baseMapper;
+
+    @Override
+    public SysTagVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    @Override
+    public TableDataInfo<SysTagVo> queryPageList(FlfTagBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SysTag> lqw = buildQueryWrapper(bo);
+        Page<SysTagVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<SysTagVo> queryList(FlfTagBo bo) {
+        LambdaQueryWrapper<SysTag> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<SysTag> buildQueryWrapper(FlfTagBo bo) {
+        LambdaQueryWrapper<SysTag> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getName()), SysTag::getName, bo.getName());
+        lqw.eq(StringUtils.isNotBlank(bo.getCategory()), SysTag::getCategory, bo.getCategory());
+        lqw.eq(bo.getStatus() != null, SysTag::getStatus, bo.getStatus());
+        lqw.orderByDesc(SysTag::getCreateTime);
+        return lqw;
+    }
+
+    @Override
+    public Boolean insertByBo(FlfTagBo bo) {
+        SysTag add = MapstructUtils.convert(bo, SysTag.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    @Override
+    public Boolean updateByBo(FlfTagBo bo) {
+        SysTag update = MapstructUtils.convert(bo, SysTag.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    private void validEntityBeforeSave(SysTag entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+}

+ 1 - 1
ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties

@@ -28,7 +28,7 @@ server.port=8848
 # nacos.inetutils.prefer-hostname-over-ip=false
 
 ### Specify local server's IP:
-nacos.inetutils.ip-address=192.168.1.140
+nacos.inetutils.ip-address=127.0.0.1
 
 spring.application.name=ruoyi-nacos
 #*************** Config Module Related Configurations ***************#

+ 11 - 0
script/config/nacos/application-common.yml

@@ -301,3 +301,14 @@ tenant:
     - sys_client
     - sys_oss_config
     - flow_spel
+    - sys_tag
+    - sys_tag_rel
+    - flf_level
+    - flf_right
+    - flf_level_right
+    - flf_fulfiller
+    - flf_audit
+    - flf_points_log
+    - flf_balance_log
+    - flf_reward_log
+    - flf_exception

+ 35 - 0
script/config/nacos/yingpaipay-archieves.yml

@@ -0,0 +1,35 @@
+spring:
+  datasource:
+    dynamic:
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driver-class-name: com.mysql.cj.jdbc.Driver
+          url: ${datasource.pet-system.url}
+          username: ${datasource.pet-system.username}
+          password: ${datasource.pet-system.password}
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: ${datasource.system-oracle.url}
+#          username: ${datasource.system-oracle.username}
+#          password: ${datasource.system-oracle.password}
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: ${datasource.system-postgres.url}
+#          username: ${datasource.system-postgres.username}
+#          password: ${datasource.system-postgres.password}
+
+# 多租户配置
+tenant:
+  # 是否开启
+  enable: true
+  # 排除表(不需要租户隔离的表)
+  excludes:
+    - sys_tag
+    - sys_tag_rel
+    - arc_change_log

+ 40 - 0
script/config/nacos/yingpaipay-fulfiller.yml

@@ -0,0 +1,40 @@
+spring:
+  datasource:
+    dynamic:
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driver-class-name: com.mysql.cj.jdbc.Driver
+          url: ${datasource.pet-system.url}
+          username: ${datasource.pet-system.username}
+          password: ${datasource.pet-system.password}
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: ${datasource.system-oracle.url}
+#          username: ${datasource.system-oracle.username}
+#          password: ${datasource.system-oracle.password}
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: ${datasource.system-postgres.url}
+#          username: ${datasource.system-postgres.username}
+#          password: ${datasource.system-postgres.password}
+
+# 多租户配置
+tenant:
+  # 是否开启
+  enable: true
+  # 排除表(不需要租户隔离的表)
+  excludes:
+    - flf_level
+    - flf_right
+    - flf_level_right
+    - flf_fulfiller
+    - flf_audit
+    - flf_points_log
+    - flf_balance_log
+    - flf_reward_log

+ 25 - 0
script/config/nacos/yingpaipay-service.yml

@@ -0,0 +1,25 @@
+spring:
+  datasource:
+    dynamic:
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driver-class-name: com.mysql.cj.jdbc.Driver
+          url: ${datasource.pet-system.url}
+          username: ${datasource.pet-system.username}
+          password: ${datasource.pet-system.password}
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: ${datasource.system-oracle.url}
+#          username: ${datasource.system-oracle.username}
+#          password: ${datasource.system-oracle.password}
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: ${datasource.system-postgres.url}
+#          username: ${datasource.system-postgres.username}
+#          password: ${datasource.system-postgres.password}