927dc4953eb5fcea1249463b6f7cf2650616963e..061a01ce068bf8e1260b6ea8e5c610737185916a
2025-06-04 xin
用户微信授权
061a01 diff | tree
2025-06-04 xin
系统:订阅消息列表
9931d6 diff | tree
2025-06-04 xin
批量短信
cf71e8 diff | tree
2025-06-04 xin
批量短信
eec809 diff | tree
2025-06-04 xin
发送短信
6ae372 diff | tree
2025-06-04 xin
短信测试
e2ba52 diff | tree
11 files added
13 files modified
719 ■■■■■ changed files
.idea/inspectionProfiles/Project_Default.xml 5 ●●●●● patch | view | raw | blame | history
oying-common/src/main/java/com/oying/utils/ConstantsKey.java 9 ●●●●● patch | view | raw | blame | history
oying-common/src/main/java/com/oying/utils/HttpRequest.java 27 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/security/rest/AuthController.java 98 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/security/rest/VerificationController.java 7 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/security/service/WeiXinService.java 36 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/security/service/dto/AuthUserWeixinDto.java 24 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/domain/User.java 12 ●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/domain/UserSubscribe.java 57 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/domain/dto/UserSubscribeQueryCriteria.java 30 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/mapper/UserMapper.java 2 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/mapper/UserSubscribeMapper.java 22 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/rest/UserSubscribeController.java 54 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/service/UserService.java 2 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/service/UserSubscribeService.java 53 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/service/impl/UserServiceImpl.java 5 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/system/service/impl/UserSubscribeServiceImpl.java 74 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/config/application-dev.yml 6 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/config/application-prod.yml 6 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/system/UserMapper.xml 12 ●●●●● patch | view | raw | blame | history
oying-system/src/main/resources/mapper/system/UserSubscribeMapper.xml 40 ●●●●● patch | view | raw | blame | history
oying-tools/src/main/java/com/oying/utils/SendMessageUtils.java 61 ●●●●● patch | view | raw | blame | history
oying-tools/src/main/java/com/oying/utils/WinnerLookEnum.java 50 ●●●●● patch | view | raw | blame | history
oying-tools/src/main/java/com/oying/utils/WinnerLookProperties.java 27 ●●●●● patch | view | raw | blame | history
.idea/inspectionProfiles/Project_Default.xml
@@ -1,6 +1,11 @@
<component name="InspectionProjectProfileManager">
  <profile version="1.0">
    <option name="myName" value="Project Default" />
    <inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
      <Languages>
        <language minSize="87" name="Java" />
      </Languages>
    </inspection_tool>
    <inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
      <option name="ADDITIONAL_TAGS" value="date,description" />
    </inspection_tool>
oying-common/src/main/java/com/oying/utils/ConstantsKey.java
@@ -2,4 +2,13 @@
public interface ConstantsKey {
    String BUYER = "BUYER";
    String MERCHANT = "MERCHANT";
    String RIDER = "RIDER";
    String ADMIN = "ADMIN";
    String SUPPLIER = "SUPPLIER";
    String AGENT = "AGENT";
    // 用户角色字典key
    String USER_TYPE_BUYER = "USER_TYPE_BUYER";
}
oying-common/src/main/java/com/oying/utils/HttpRequest.java
@@ -14,6 +14,7 @@
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
@@ -22,6 +23,8 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
@@ -79,6 +82,28 @@
        return headers;
    }
    private static HttpHeaders getMsg() {
        HttpHeaders headers = new HttpHeaders();
        List<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.ALL);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 表单格式
        headers.setAccept(mediaTypes);
        headers.setConnection("Keep-Alive");
        headers.set("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        return headers;
    }
    /**
     * 短信
     * http返回String xml
     */
    public static String exchangeMsg(HttpMethod httpMethod, String url, MultiValueMap<String, String> map) {
        HttpHeaders headers = getMsg();
        HttpEntity<Object> httpEntity = new HttpEntity<>(map, headers);
        RestTemplate restTemplate = new RestTemplate(getFactory());
        return restTemplate.exchange(url, httpMethod, httpEntity, String.class).getBody();
    }
    /**
     * http返回String
     */
@@ -98,4 +123,6 @@
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange(url, httpMethod, httpEntity, JSONObject.class).getBody();
    }
}
oying-system/src/main/java/com/oying/modules/security/rest/AuthController.java
@@ -1,6 +1,7 @@
package com.oying.modules.security.rest;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSONObject;
import com.oying.modules.security.config.CaptchaConfig;
import com.oying.modules.security.config.LoginProperties;
import com.oying.modules.security.config.SecurityProperties;
@@ -8,8 +9,15 @@
import com.oying.modules.security.security.TokenProvider;
import com.oying.modules.security.service.OnlineUserService;
import com.oying.modules.security.service.UserDetailsServiceImpl;
import com.oying.modules.security.service.WeiXinService;
import com.oying.modules.security.service.dto.AuthUserDto;
import com.oying.modules.security.service.dto.AuthUserWeixinDto;
import com.oying.modules.security.service.dto.JwtUserDto;
import com.oying.modules.system.domain.DictDetail;
import com.oying.modules.system.domain.Role;
import com.oying.modules.system.domain.User;
import com.oying.modules.system.service.DictDetailService;
import com.oying.modules.system.service.UserService;
import com.oying.utils.*;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
@@ -27,14 +35,12 @@
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
@@ -57,9 +63,12 @@
    private final LoginProperties loginProperties;
    private final PasswordEncoder passwordEncoder;
    private final UserDetailsServiceImpl userDetailsService;
    private final WeiXinService weiXinService;
    private final UserService userService;
    private final DictDetailService dictDetailService;
    @Log("用户登录")
    @ApiOperation("登录授权")
    @Log("账号密码登录")
    @ApiOperation("账号密码登录")
    @AnonymousPostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
@@ -99,6 +108,85 @@
        return ResponseEntity.ok(R.success(authInfo));
    }
    @Log("微信授权登录")
    @ApiOperation("微信授权登录")
    @AnonymousPostMapping(value = "/login/weixin")
    public ResponseEntity<Object> loginWeixin(@Validated @RequestBody AuthUserWeixinDto authUser, HttpServletRequest request) throws Exception {
        JSONObject jsonObject;
        switch (authUser.getCode()) {
            case "OLD":
                jsonObject = weiXinService.code2Session(authUser.getCode());
                String openid = jsonObject.getString("openid");
                User userDto = userService.findByOpenid(openid);
                if (userDto == null) {
                    Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
                        put("token", openid);
                        put("member", null);
                    }};
                    return ResponseEntity.ok(authInfo);
                }
                authUser.setUsername(userDto.getUsername());
                break;
            case "NEW":
                jsonObject = weiXinService.getPhoneNumber(authUser.getCode());
                String phone = jsonObject.getJSONObject("phone_info").getString("purePhoneNumber");
                User user1 = userService.findByName(phone);
                if (user1 == null) {
                    //创建用户
                    User user = new User();
                    user.setUsername(phone);
                    user.setNickName("LYHD-" + phone);
                    user.setUserType(ConstantsKey.BUYER);
                    user.setEnabled(true);
                    getRole(user);
                    user.setPassword(passwordEncoder.encode(phone.substring(phone.length() - 6)));
                    user.setOpenid(authUser.getUsername());
                    userService.create(user);
                } else {
                    if (user1.getUserType().equals(ConstantsKey.BUYER)) {
                        if (user1.getRoles() == null) {
                            getRole(user1);
                        }
                    }
                    user1.setOpenid(authUser.getUsername());
                    userService.update(user1);
                }
                authUser.setUsername(phone);
                break;
            default:
                throw new BadRequestException("登录类型错误");
        }
        // 获取用户信息
        JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername());
        Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(jwtUser);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUser);
        }};
        if (loginProperties.isSingleLogin()) {
            // 踢掉之前已经登录的token
            onlineUserService.kickOutForUsername(authUser.getUsername());
        }
        // 保存在线信息
        onlineUserService.save(jwtUser, token, request);
        // 返回登录信息
        return ResponseEntity.ok(R.success(authInfo));
    }
    public void getRole(User user1) {
        Set<Role> roles = new HashSet<>();
        Role role = new Role();
        DictDetail detail = dictDetailService.getDictByName(ConstantsKey.USER_TYPE_BUYER).get(0);
        role.setId(Long.valueOf(detail.getLabel()));
        roles.add(role);
        user1.setRoles(roles);
    }
    @ApiOperation("临时授权")
    @AnonymousGetMapping(value = "/token")
    public ResponseEntity<Object> loginTest(@RequestParam String username, HttpServletRequest request) {
oying-system/src/main/java/com/oying/modules/security/rest/VerificationController.java
@@ -4,6 +4,8 @@
import com.oying.annotation.rest.AnonymousGetMapping;
import com.oying.utils.R;
import com.oying.utils.RedisUtils;
import com.oying.utils.SendMessageUtils;
import com.oying.utils.WinnerLookProperties;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@@ -31,15 +33,18 @@
    private String key;
    @Value("${sms.time}")
    private Long time;
    private final WinnerLookProperties properties;
    @AnonymousGetMapping
    @ApiOperation("短信验证码")
    public ResponseEntity<Object> toPayAsPc(@RequestParam String phone) {
    public ResponseEntity<Object> verification(@RequestParam String phone) {
        String uuid = key + IdUtil.simpleUUID();
        System.out.println(phone);
        //创建验证码
        String verification = (int) ((Math.random() * 9 + 1) * 100000) + "";
        redisUtils.set(uuid, verification, time);
        String message = "您的验证码为:" + verification + ",请勿泄露于他人!";
        SendMessageUtils.sendMsg(properties.getUrlSendMsg(), phone, message, properties);
        return ResponseEntity.ok(R.success(uuid));
    }
}
oying-system/src/main/java/com/oying/modules/security/service/WeiXinService.java
@@ -2,6 +2,10 @@
import com.alibaba.fastjson2.JSONObject;
import com.oying.modules.security.config.WeiXinProperties;
import com.oying.modules.system.domain.User;
import com.oying.modules.system.domain.UserSubscribe;
import com.oying.modules.system.service.UserService;
import com.oying.modules.system.service.UserSubscribeService;
import com.oying.utils.HttpRequest;
import com.oying.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
@@ -24,6 +28,10 @@
    private RedisUtils redisUtils;
    @Value("${wx.enabled}")
    private Boolean wxEnabled;
    @Resource
    private UserSubscribeService subscribeService;
    @Resource
    private UserService userService;
    /**
     * POST 获取稳定版接口调用凭据 获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存。
@@ -81,22 +89,30 @@
    /**
     * POST 该接口用于发送订阅消息。
     *
     * @param data 请求参数
     * @param openId 用户openId
     * @param data       请求参数
     * @param openid     用户openId
     * @param templateId 订阅模板id
     * @param page 小程序跳转链接
     * @return JSONObject
     * @param page       小程序跳转链接
     */
    public JSONObject sendMessage(Map<String, Object> data, String openId, String templateId, String page) {
    public void sendMessage(Map<String, Object> data, String openid, String templateId, String page) {
        JSONObject jsonObject = new JSONObject();
        if (wxEnabled) {
            String url = weiXinProperties.getSendMessage();
            url = url.replace("{accessToken}", getStableAccessToken());
            Map<String, Object> map = getSendMessageDto(data, openId, templateId, page);
            return HttpRequest.exchangeJsonObject(HttpMethod.POST, url, map);
            Map<String, Object> map = getSendMessageDto(data, openid, templateId, page);
            jsonObject = HttpRequest.exchangeJsonObject(HttpMethod.POST, url, map);
        } else {
            jsonObject.put("message", "测试环境");
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("message", "测试环境");
        return jsonObject;
        User user = userService.findByOpenid(openid);
        UserSubscribe sub = new UserSubscribe();
        sub.setSubType(templateId);
        sub.setUserId(user.getId());
        sub.setOpenid(openid);
        sub.setUsername(user.getUsername());
        sub.setSendMessage(JSONObject.toJSONString(data));
        sub.setSubMessage(jsonObject.toJSONString());
        subscribeService.save(sub);
    }
    private Map<String, Object> getSendMessageDto(Map<String, Object> data, String openId, String templateId, String page) {
oying-system/src/main/java/com/oying/modules/security/service/dto/AuthUserWeixinDto.java
New file
@@ -0,0 +1,24 @@
package com.oying.modules.security.service.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
 * @author xin
 * @description
 * @date 2025/6/4 19:32
 */
@Getter
@Setter
public class AuthUserWeixinDto {
    @ApiModelProperty(value = "用户名:type为NEW时传入用户openid")
    private String username;
    @ApiModelProperty(value = "微信code")
    private String code;
    @ApiModelProperty(value = "类型默认:OLD,注册:NEW",example = "OLD")
    private String type = "OLD";
}
oying-system/src/main/java/com/oying/modules/system/domain/User.java
@@ -8,6 +8,7 @@
import lombok.Getter;
import lombok.Setter;
import com.oying.base.BaseEntity;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@@ -26,7 +27,7 @@
public class User extends BaseEntity implements Serializable {
    @NotNull(groups = Update.class)
    @TableId(value="user_id", type = IdType.AUTO)
    @TableId(value = "user_id", type = IdType.AUTO)
    @ApiModelProperty(value = "ID", hidden = true)
    private Long id;
@@ -45,6 +46,13 @@
    @ApiModelProperty(value = "用户机构")
    @TableField(exist = false)
    private Dept dept;
    @NotBlank
    @ApiModelProperty(value = "用户类型(BUYER、MERCHANT、RIDER、ADMIN、SUPPLIER、AGENT)")
    private String userType;
    @ApiModelProperty(value = "openid")
    private String openid;
    @NotBlank
    @ApiModelProperty(value = "用户名称")
@@ -66,7 +74,7 @@
    @ApiModelProperty(value = "用户性别")
    private String gender;
    @ApiModelProperty(value = "头像真实名称",hidden = true)
    @ApiModelProperty(value = "头像真实名称", hidden = true)
    private String avatarName;
    @ApiModelProperty(value = "头像存储的路径", hidden = true)
oying-system/src/main/java/com/oying/modules/system/domain/UserSubscribe.java
New file
@@ -0,0 +1,57 @@
package com.oying.modules.system.domain;
import com.oying.base.BaseEntity;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
/**
* @description /
* @author lixin
* @date 2025-06-04
**/
@Getter
@Setter
@TableName("sys_user_subscribe")
public class UserSubscribe extends BaseEntity implements Serializable {
    @TableId(value = "sub_id", type = IdType.AUTO)
    @ApiModelProperty(value = "主键")
    private Long subId;
    @NotBlank
    @ApiModelProperty(value = "消息类型")
    private String subType;
    @NotNull
    @ApiModelProperty(value = "用户ID")
    private Long userId;
    @NotBlank
    @ApiModelProperty(value = "openid")
    private String openid;
    @NotBlank
    @ApiModelProperty(value = "账号")
    private String username;
    @NotBlank
    @ApiModelProperty(value = "发送消息")
    private String sendMessage;
    @NotBlank
    @ApiModelProperty(value = "返回消息")
    private String subMessage;
    public void copy(UserSubscribe source){
        BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
    }
}
oying-system/src/main/java/com/oying/modules/system/domain/dto/UserSubscribeQueryCriteria.java
New file
@@ -0,0 +1,30 @@
package com.oying.modules.system.domain.dto;
import lombok.Data;
import java.sql.Timestamp;
import java.util.List;
import io.swagger.annotations.ApiModelProperty;
/**
* @author lixin
* @date 2025-06-04
**/
@Data
public class UserSubscribeQueryCriteria{
    @ApiModelProperty(value = "页码", example = "1")
    private Integer page = 1;
    @ApiModelProperty(value = "每页数据量", example = "10")
    private Integer size = 10;
    @ApiModelProperty(value = "消息类型")
    private String subType;
    @ApiModelProperty(value = "openid")
    private String openid;
    @ApiModelProperty(value = "手机号")
    private String phone;
    private List<Timestamp> createTime;
}
oying-system/src/main/java/com/oying/modules/system/mapper/UserMapper.java
@@ -27,6 +27,8 @@
    User findByUsername(@Param("username") String username);
    User findByOpenid(@Param("openid") String openid);
    User findByEmail(@Param("email") String email);
    User findByPhone(@Param("phone") String phone);
oying-system/src/main/java/com/oying/modules/system/mapper/UserSubscribeMapper.java
New file
@@ -0,0 +1,22 @@
package com.oying.modules.system.mapper;
import com.oying.modules.system.domain.UserSubscribe;
import com.oying.modules.system.domain.dto.UserSubscribeQueryCriteria;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* @author lixin
* @date 2025-06-04
**/
@Mapper
public interface UserSubscribeMapper extends BaseMapper<UserSubscribe> {
    IPage<UserSubscribe> findAll(@Param("criteria") UserSubscribeQueryCriteria criteria, Page<Object> page);
    List<UserSubscribe> findAll(@Param("criteria") UserSubscribeQueryCriteria criteria);
}
oying-system/src/main/java/com/oying/modules/system/rest/UserSubscribeController.java
New file
@@ -0,0 +1,54 @@
package com.oying.modules.system.rest;
import com.oying.annotation.Log;
import com.oying.modules.system.domain.UserSubscribe;
import com.oying.modules.system.service.UserSubscribeService;
import com.oying.modules.system.domain.dto.UserSubscribeQueryCriteria;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oying.utils.PageResult;
/**
* @author lixin
* @date 2025-06-04
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "系统:订阅消息列表")
@RequestMapping("/api/userSubscribe")
public class UserSubscribeController {
    private final UserSubscribeService userSubscribeService;
    @ApiOperation("导出数据")
    @GetMapping(value = "/download")
    @PreAuthorize("@el.check('userSubscribe:list')")
    public void exportUserSubscribe(HttpServletResponse response, UserSubscribeQueryCriteria criteria) throws IOException {
        userSubscribeService.download(userSubscribeService.queryAll(criteria), response);
    }
    @GetMapping
    @ApiOperation("查询订阅消息")
    @PreAuthorize("@el.check('userSubscribe:list')")
    public ResponseEntity<PageResult<UserSubscribe>> queryUserSubscribe(UserSubscribeQueryCriteria criteria){
        Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
        return new ResponseEntity<>(userSubscribeService.queryAll(criteria,page),HttpStatus.OK);
    }
    @DeleteMapping
    @Log("删除订阅消息")
    @ApiOperation("删除订阅消息")
    @PreAuthorize("@el.check('userSubscribe:del')")
    public ResponseEntity<Object> deleteUserSubscribe(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
        userSubscribeService.deleteAll(ids);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
oying-system/src/main/java/com/oying/modules/system/service/UserService.java
@@ -53,6 +53,8 @@
     */
    User findByName(String userName);
    User findByOpenid(String openId);
    /**
     * 根据用户名查询
     * @param userName /
oying-system/src/main/java/com/oying/modules/system/service/UserSubscribeService.java
New file
@@ -0,0 +1,53 @@
package com.oying.modules.system.service;
import com.oying.modules.system.domain.UserSubscribe;
import com.oying.modules.system.domain.dto.UserSubscribeQueryCriteria;
import java.util.List;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.oying.utils.PageResult;
/**
* @description 服务接口
* @author lixin
* @date 2025-06-04
**/
public interface UserSubscribeService extends IService<UserSubscribe> {
    /**
    * 查询数据分页
    * @param criteria 条件
    * @param page 分页参数
    * @return PageResult
    */
    PageResult<UserSubscribe> queryAll(UserSubscribeQueryCriteria criteria, Page<Object> page);
    /**
    * 查询所有数据不分页
    * @param criteria 条件参数
    * @return List<UserSubscribeDto>
    */
    List<UserSubscribe> queryAll(UserSubscribeQueryCriteria criteria);
    /**
    * 创建
    * @param resources /
    */
    void create(UserSubscribe resources);
    /**
    * 多选删除
    * @param ids /
    */
    void deleteAll(List<Long> ids);
    /**
    * 导出数据
    * @param all 待导出的数据
    * @param response /
    * @throws IOException /
    */
    void download(List<UserSubscribe> all, HttpServletResponse response) throws IOException;
}
oying-system/src/main/java/com/oying/modules/system/service/impl/UserServiceImpl.java
@@ -178,6 +178,11 @@
    }
    @Override
    public User findByOpenid(String openid) {
        return userMapper.findByOpenid(openid);
    }
    @Override
    public User getLoginData(String userName) {
        return userMapper.findByUsername(userName);
    }
oying-system/src/main/java/com/oying/modules/system/service/impl/UserSubscribeServiceImpl.java
New file
@@ -0,0 +1,74 @@
package com.oying.modules.system.service.impl;
import com.oying.modules.system.domain.UserSubscribe;
import com.oying.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oying.modules.system.service.UserSubscribeService;
import com.oying.modules.system.domain.dto.UserSubscribeQueryCriteria;
import com.oying.modules.system.mapper.UserSubscribeMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oying.utils.PageUtil;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import com.oying.utils.PageResult;
/**
* @description 服务实现
* @author lixin
* @date 2025-06-04
**/
@Service
@RequiredArgsConstructor
public class UserSubscribeServiceImpl extends ServiceImpl<UserSubscribeMapper, UserSubscribe> implements UserSubscribeService {
    private final UserSubscribeMapper userSubscribeMapper;
    @Override
    public PageResult<UserSubscribe> queryAll(UserSubscribeQueryCriteria criteria, Page<Object> page){
        return PageUtil.toPage(userSubscribeMapper.findAll(criteria, page));
    }
    @Override
    public List<UserSubscribe> queryAll(UserSubscribeQueryCriteria criteria){
        return userSubscribeMapper.findAll(criteria);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(UserSubscribe resources) {
        userSubscribeMapper.insert(resources);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        userSubscribeMapper.deleteBatchIds(ids);
    }
    @Override
    public void download(List<UserSubscribe> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (UserSubscribe userSubscribe : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("消息类型", userSubscribe.getSubType());
            map.put("用户ID", userSubscribe.getUserId());
            map.put("openid", userSubscribe.getOpenid());
            map.put("手机号", userSubscribe.getUsername());
            map.put("发送消息", userSubscribe.getSendMessage());
            map.put("返回消息", userSubscribe.getSubMessage());
            map.put("创建者", userSubscribe.getCreateBy());
            map.put("创建时间", userSubscribe.getCreateTime());
            map.put("修改者", userSubscribe.getUpdateBy());
            map.put("修改时间", userSubscribe.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
oying-system/src/main/resources/config/application-dev.yml
@@ -176,3 +176,9 @@
  access_key_secret: QpE58YEFtgoIwUoGNlN5JlNY7t6qVu7vMkix8gAI
  bucket: oying
  endpoint: https://obs.cn-southwest-2.myhuaweicloud.com
winner-look:
  url-send-msg: https://118.178.116.15:8443/winnerrxd/api/trigger/SendMsg
  url-send-msg-batch: https://118.178.116.15:8443/winnerrxd/api/trigger/SendBatchMsg
  user-code: CQLYSXYJ
  user-pass: lych1205!
oying-system/src/main/resources/config/application-prod.yml
@@ -186,3 +186,9 @@
  access_key_secret: QpE58YEFtgoIwUoGNlN5JlNY7t6qVu7vMkix8gAI
  bucket: oying
  endpoint: https://obs.cn-southwest-2.myhuaweicloud.com
winner-look:
  url-send-msg: https://118.178.116.15:8443/winnerrxd/api/trigger/SendMsg
  url-send-msg-batch: https://118.178.116.15:8443/winnerrxd/api/trigger/SendBatchMsg
  user-code: CQLYSXYJ
  user-pass: lych1205!
oying-system/src/main/resources/mapper/system/UserMapper.xml
@@ -5,6 +5,8 @@
        <id column="user_user_id" property="id"/>
        <result column="user_dept_id" property="deptId"/>
        <result column="user_username" property="username"/>
        <result column="user_user_type" property="userType"/>
        <result column="user_openid" property="openid"/>
        <result column="user_nick_name" property="nickName"/>
        <result column="user_email" property="email"/>
        <result column="user_phone" property="phone"/>
@@ -37,6 +39,7 @@
    <sql id="Base_Column_List">
        u.user_id as user_user_id, u.dept_id as user_dept_id, u.username as user_username,
        u.user_type as user_user_type,u.openid as user_openid,
           u.nick_name as user_nick_name, u.email as user_email, u.phone as user_phone,
           u.gender as user_gender, u.avatar_name as user_avatar_name, u.avatar_path as user_avatar_path,
           u.enabled as user_enabled, u.pwd_reset_time as user_pwd_reset_time, u.create_by as user_create_by,
@@ -116,6 +119,15 @@
        where u.username = #{username}
    </select>
    <select id="findByOpenid" resultMap="BaseResultMap">
        select
        u.password user_password, u.is_admin user_is_admin,
        <include refid="Base_Column_List"/>
        from sys_user u
        left join sys_dept d on u.dept_id = d.dept_id
        where u.openid = #{openid}
    </select>
    <select id="findByEmail" resultType="com.oying.modules.system.domain.User">
        select user_id as id, username from sys_user
        where email = #{email}
oying-system/src/main/resources/mapper/system/UserSubscribeMapper.xml
New file
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.oying.modules.system.mapper.UserSubscribeMapper">
    <resultMap id="BaseResultMap" type="com.oying.modules.system.domain.UserSubscribe">
        <id column="sub_id" property="subId"/>
        <result column="sub_type" property="subType"/>
        <result column="user_id" property="userId"/>
        <result column="openid" property="openid"/>
        <result column="username" property="username"/>
        <result column="send_message" property="sendMessage"/>
        <result column="sub_message" property="subMessage"/>
        <result column="create_by" property="createBy"/>
        <result column="create_time" property="createTime"/>
        <result column="update_by" property="updateBy"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>
    <sql id="Base_Column_List">
        sub_id, sub_type, user_id, openid, username, send_message, sub_message, create_by, create_time, update_by, update_time
    </sql>
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from sys_user_subscribe
        <where>
            <if test="criteria.subType != null">
                and sub_type = #{criteria.subType}
            </if>
            <if test="criteria.blurry != null and criteria.blurry != ''">
                and (openid like concat('%',#{criteria.openid},'%')
                or username like concat('%',#{criteria.username},'%'))
            </if>
            <if test="criteria.createTime != null and criteria.createTime.size() > 0">
                AND create_time BETWEEN #{criteria.createTime[0]} AND #{criteria.createTime[1]}
            </if>
        </where>
        order by sub_id desc
    </select>
</mapper>
oying-tools/src/main/java/com/oying/utils/SendMessageUtils.java
New file
@@ -0,0 +1,61 @@
package com.oying.utils;
import com.oying.exception.BadRequestException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import java.util.HashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @author xin
 * @description
 * @date 2025/6/4 01:10
 */
@Slf4j
@Data
public class SendMessageUtils {
    public static void sendMsg(String url, String phone, String message, WinnerLookProperties properties) {
        Map<String, String> params = new HashMap<>();
        params.put("userCode", properties.getUserCode());
        params.put("userPass", properties.getUserPass());
        params.put("DesNo", phone);
        params.put("Msg", message + "【立研】");
        params.put("smsType", "101");
        String str = HttpRequest.exchangeMsg(HttpMethod.POST, url, convert(params));
        if (ObjectUtils.isEmpty(extractWithRegex(str))) {
            log.error("短信调用异常 {}", str);
            throw new BadRequestException("短信调用异常");
        }
        int i = Integer.parseInt(str);
        if (i < 0) {
            throw new BadRequestException(WinnerLookEnum.find(i));
        }
    }
    public static String extractWithRegex(String xml) {
        Pattern pattern = Pattern.compile(
                "<string[^>]*>([^<]*)</string>"
        );
        Matcher matcher = pattern.matcher(xml);
        return matcher.find() ? matcher.group(1) : null;
    }
    /**
     * Map转MultiValueMap
     */
    public static <K, V> MultiValueMap<K, V> convert(Map<K, V> map) {
        MultiValueMap<K, V> multiValueMap = new LinkedMultiValueMap<>();
        map.forEach(multiValueMap::add);
        return multiValueMap;
    }
}
oying-tools/src/main/java/com/oying/utils/WinnerLookEnum.java
New file
@@ -0,0 +1,50 @@
package com.oying.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum WinnerLookEnum {
    ONE(-1, "应用程序错误"),
    THREE(-3, "用户名或密码错误"),
    FOUR(-4, "短信内容和备案的模板不一样"),
    FIVE(-5, "签名不正确"),
    SEVEN(-7, "余额不足"),
    EIGHT(-8, "无可用通道或不在通道时间范围"),
    NINE(-9, "无效号码"),
    TEN(-10, "签名内容不符合长度"),
    ELEVEN(-11, "用户有效期过期"),
    TWELVE(-12, "黑名单"),
    SIXTEEN(-16, "接口请求过于频繁,余额接口 5s 秒一次,其他接口适当调整"),
    SEVENTEEN(-17, "非法 IP"),
    EIGHTEEN(-18, "Msg 内容格式错误"),
    NINETEEN(-19, "短信数量错误,小于 1 /大于 500(个性化)/大于 1000(群发)"),
    TWENTY(-20, "号码错误或者黑名单"),
    TWENTY_THREE(-23, "解密失败"),
    TWENTY_FOUR(-24, "短信包含用户敏感信息"),
    TWENTY_FIVE(-25, "用户被冻结"),
    TWENTY_SIX(-26, "无效数据"),
    TWENTY_SEVEN(-27, "请求参数错误"),
    TWENTY_EIGHT(-28, "无效数据"),
    FORTY_ONE(-41, "指定短信模板类型错误或短信类型参数错误"),
    FORTY_FOUR(-44, "自定义扩展号不符合规则(1-16 位数字)"),
    FORTY_SIX(-46, "用户黑名单"),
    FORTY_SEVEN(-47, "系统黑名单"),
    FORTY_EIGHT(-48, "号码超频拦截"),
    FIFTY_ONE(-51, "超过设置的每月短信条数的限制"),
    FIFTY_FIVE(-54, "短信包含系统敏感信息"),
    ZERO(0, "未知错误");
    private final int key;
    private final String value;
    public static String find(Integer key) {
        for (WinnerLookEnum value : WinnerLookEnum.values()) {
            if (value.getKey() == key) {
                return value.getValue();
            }
        }
        return ZERO.getValue();
    }
}
oying-tools/src/main/java/com/oying/utils/WinnerLookProperties.java
New file
@@ -0,0 +1,27 @@
package com.oying.utils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * @author xin
 * @description
 * @date 2025/6/4 14:32
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "winner-look")
public class WinnerLookProperties {
    private String urlSendMsg;
    /**
     * 批量短信 手机号码,必填
     * 多个号码用英文半角逗号分隔。如 :
     * 13900000000,13900000001
     * 每次不要超过50个号码
     */
    private String urlSendMsgBatch;
    private String userCode;
    private String userPass;
}