diff --git a/src/main/java/com/ycwl/basic/Application.java b/src/main/java/com/ycwl/basic/Application.java index dc94933..02456b2 100644 --- a/src/main/java/com/ycwl/basic/Application.java +++ b/src/main/java/com/ycwl/basic/Application.java @@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "com.ycwl.basic.mapper") +@MapperScan(basePackages = "com.ycwl.basic.*.mapper") public class Application { public static void main(String[] args) { diff --git a/src/main/java/com/ycwl/basic/profitsharing/biz/ProfitSharingBiz.java b/src/main/java/com/ycwl/basic/profitsharing/biz/ProfitSharingBiz.java new file mode 100644 index 0000000..d7805ff --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/biz/ProfitSharingBiz.java @@ -0,0 +1,84 @@ +package com.ycwl.basic.profitsharing.biz; + +import com.ycwl.basic.profitsharing.entity.ProfitSharingConfig; +import com.ycwl.basic.profitsharing.entity.ProfitSharingRecord; +import com.ycwl.basic.profitsharing.entity.ProfitSharingUser; +import com.ycwl.basic.profitsharing.mapper.ProfitSharingConfigMapper; +import com.ycwl.basic.profitsharing.mapper.ProfitSharingRecordMapper; +import com.ycwl.basic.profitsharing.repository.ProfitSharingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Component +public class ProfitSharingBiz { + @Autowired + private ProfitSharingRepository repository; + + @Autowired + private ProfitSharingRecordMapper recordMapper; + + public void processProfitSharing(Long scenicId, Long orderId, BigDecimal amount) { + ProfitSharingConfig config = repository.getScenicConfig(scenicId); + if (config == null || config.getUsers() == null || config.getUsers().isEmpty()) { + return; + } + + List records = new ArrayList<>(); + BigDecimal totalPercentage = BigDecimal.ZERO; + for (ProfitSharingUser user : config.getUsers()) { + totalPercentage = totalPercentage.add(user.getRealRate()); + } + if (totalPercentage.compareTo(BigDecimal.valueOf(100)) > 0) { + throw new RuntimeException("分账比例总和超过100%"); + } + BigDecimal myPercentage = BigDecimal.valueOf(100).subtract(totalPercentage); + + for (ProfitSharingUser user : config.getUsers()) { + BigDecimal userAmount = amount.multiply(user.getRealRate()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_DOWN); + BigDecimal wxAmount = amount.multiply(user.getWxRate()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_DOWN); + BigDecimal manualAmount = userAmount.subtract(wxAmount); + ProfitSharingRecord record = new ProfitSharingRecord(); + record.setScenicId(scenicId); + record.setOrderId(orderId); + record.setUserId(user.getId()); + record.setUserName(user.getName()); + record.setAmount(userAmount); + record.setWxAmount(wxAmount); + record.setManualAmount(manualAmount); + record.setWxRate(user.getWxRate()); + record.setRealRate(user.getRealRate()); + record.setOrderAmount(amount); + record.setCreateTime(new Date()); + records.add(record); + } + BigDecimal remainAmount = amount; + for (ProfitSharingRecord record : records) { + remainAmount = remainAmount.subtract(record.getAmount()); + } + + if (remainAmount.compareTo(BigDecimal.ZERO) > 0) { + ProfitSharingRecord record = new ProfitSharingRecord(); + record.setScenicId(scenicId); + record.setOrderId(orderId); + record.setUserId(0L); + record.setUserName("自己"); + record.setAmount(remainAmount); + record.setWxAmount(BigDecimal.ZERO); + record.setManualAmount(remainAmount); + record.setWxRate(BigDecimal.ZERO); + record.setRealRate(myPercentage); + record.setOrderAmount(amount); + record.setCreateTime(new Date()); + records.add(record); + } + + recordMapper.batchInsert(records); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/profitsharing/controller/ProfitSharingController.java b/src/main/java/com/ycwl/basic/profitsharing/controller/ProfitSharingController.java new file mode 100644 index 0000000..ef41564 --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/controller/ProfitSharingController.java @@ -0,0 +1,108 @@ +package com.ycwl.basic.profitsharing.controller; + +import com.ycwl.basic.profitsharing.entity.ProfitSharingConfig; +import com.ycwl.basic.profitsharing.entity.ProfitSharingUser; +import com.ycwl.basic.profitsharing.mapper.ProfitSharingConfigMapper; +import com.ycwl.basic.profitsharing.mapper.ProfitSharingUserMapper; +import com.ycwl.basic.utils.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.List; + +@RestController +@RequestMapping("/api/profitSharingConfig/v1") +public class ProfitSharingController { + + @Autowired + private ProfitSharingConfigMapper configMapper; + @Autowired + private ProfitSharingUserMapper userMapper; + + // 增加: 插入新的ProfitSharingConfig记录 + @PostMapping("/add") + public ApiResponse addConfig(@RequestBody ProfitSharingConfig config) { + // 检查是否存在相同 scenicId 的配置 + ProfitSharingConfig existingConfig = configMapper.findByScenicId(config.getScenicId()); + if (existingConfig != null) { + return ApiResponse.fail("该景区已存在配置"); + } + configMapper.insert(config); + // 获取生成的 configId + Long configId = config.getId(); + // 插入 users 列表 + if (config.getUsers() != null) { + // 提前检查,避免realRate总和超过100 + BigDecimal totalPercentage = BigDecimal.ZERO; + for (ProfitSharingUser user : config.getUsers()) { + totalPercentage = totalPercentage.add(user.getRealRate()); + } + if (totalPercentage.compareTo(BigDecimal.valueOf(100)) > 0) { + return ApiResponse.fail("分账比例总和超过100%"); + } + for (ProfitSharingUser user : config.getUsers()) { + user.setConfigId(configId); + userMapper.insert(user); + } + } + return ApiResponse.success("配置添加成功"); + } + + @DeleteMapping("/delete/{id}") + public ApiResponse deleteConfig(@PathVariable Long id) { + configMapper.deleteById(id); + return ApiResponse.success("配置删除成功"); + } + + // 更新: 更新指定ID的ProfitSharingConfig记录 + @PutMapping("/update") + public ApiResponse updateConfig(@RequestBody ProfitSharingConfig config) { + // 更新 ProfitSharingConfig 记录 + configMapper.update(config); + // 获取 configId + Long configId = config.getId(); + if (configId == null) { + return ApiResponse.fail("configId不能为空"); + } + // 插入新的 users 列表 + if (config.getUsers() != null) { + // 提前检查,避免realRate总和超过100 + BigDecimal totalPercentage = BigDecimal.ZERO; + for (ProfitSharingUser user : config.getUsers()) { + totalPercentage = totalPercentage.add(user.getRealRate()); + } + if (totalPercentage.compareTo(BigDecimal.valueOf(100)) > 0) { + return ApiResponse.fail("分账比例总和超过100%"); + } + // 删除原有 users 列表 + userMapper.deleteByConfigId(configId); + for (ProfitSharingUser user : config.getUsers()) { + user.setConfigId(configId); + userMapper.insert(user); + } + } + return ApiResponse.success("配置更新成功"); + } + + @GetMapping("/findById/{id}") + public ApiResponse findById(@PathVariable Long id) { + ProfitSharingConfig config = configMapper.findById(id); + config.setUsers(userMapper.findByConfigId(id)); + return ApiResponse.success(config); + } + + // 查询: 查询所有记录 + @GetMapping("/findAll") + public ApiResponse findAllConfigs() { + List configs = configMapper.findAll(); + return ApiResponse.success(configs); + } + + // 查询: 根据scenicId查询单个记录 + @GetMapping("/findByScenicId/{scenicId}") + public ApiResponse findByScenicId(@PathVariable Long scenicId) { + ProfitSharingConfig config = configMapper.findByScenicId(scenicId); + return ApiResponse.success(config); + } +} diff --git a/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingConfig.java b/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingConfig.java index 9850314..46be105 100644 --- a/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingConfig.java +++ b/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingConfig.java @@ -2,11 +2,16 @@ package com.ycwl.basic.profitsharing.entity; import lombok.Data; +import java.util.Date; import java.util.List; @Data public class ProfitSharingConfig { - private boolean enabled; + private Long id; private Long scenicId; - private List profitSharingList; + private String name; + private String description; + private Date createTime; + private Date updateTime; + private List users; } diff --git a/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingRecord.java b/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingRecord.java new file mode 100644 index 0000000..86b5e33 --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingRecord.java @@ -0,0 +1,22 @@ +package com.ycwl.basic.profitsharing.entity; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class ProfitSharingRecord { + private Long id; + private Long scenicId; + private Long orderId; + private Long userId; + private String userName; + private BigDecimal amount; + private BigDecimal wxAmount; + private BigDecimal manualAmount; + private BigDecimal wxRate; // 微信分账比例 + private BigDecimal realRate; // 实际分账比例 + private BigDecimal orderAmount; // 订单金额 + private Date createTime; +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingUser.java b/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingUser.java index 2bf06bd..f8c5d7f 100644 --- a/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingUser.java +++ b/src/main/java/com/ycwl/basic/profitsharing/entity/ProfitSharingUser.java @@ -1,15 +1,35 @@ package com.ycwl.basic.profitsharing.entity; -import com.ycwl.basic.profitsharing.enums.ProfitSharingUserType; +import com.alibaba.fastjson.JSON; +import com.ycwl.basic.profitsharing.enums.ProfitSharingWxPayType; import lombok.Data; import java.math.BigDecimal; +import java.util.Map; @Data public class ProfitSharingUser { - private ProfitSharingUserType type; - private Integer amount; + private Long id; + private Long configId; + private ProfitSharingWxPayType wxPayType; + private Map wxPayConfig; + private String name; private String description; + /** + * 微信分账比例,单位为% + */ private BigDecimal wxRate; + /** + * 实际分账比例,单位为% + */ private BigDecimal realRate; -} + private BigDecimal orderAmount; // 订单金额 + + public void setWxPayConfig(String wxPayConfig) { + this.wxPayConfig = JSON.parseObject(wxPayConfig, Map.class); + } + + public String getWxPayConfig() { + return JSON.toJSONString(wxPayConfig); + } +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingUserType.java b/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingUserType.java index 5f80dcc..bbf834e 100644 --- a/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingUserType.java +++ b/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingUserType.java @@ -1,23 +1,16 @@ package com.ycwl.basic.profitsharing.enums; public enum ProfitSharingUserType { - MERCHANT("MERCHANT_ID", "商户号"), - PERSON_SUB("PERSONAL_SUB_OPENID", "子商户个人"), - PERSON_PARENT("PERSONAL_OPENID", "父商户个人"); - - private final String code; - private final String desc; + SCENIC("SCENIC", "景区方"), + AGENT("AGENT", "代理商方"), + PROVIDER("PROVIDER", "技术提供方"), + SELF("SELF", "自己"), + ; + public final String code; + public final String desc; ProfitSharingUserType(String code, String desc) { this.code = code; this.desc = desc; } - - public String getCode() { - return code; - } - - public String getDesc() { - return desc; - } } diff --git a/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingWxPayType.java b/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingWxPayType.java new file mode 100644 index 0000000..f6b7f02 --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/enums/ProfitSharingWxPayType.java @@ -0,0 +1,24 @@ +package com.ycwl.basic.profitsharing.enums; + +public enum ProfitSharingWxPayType { + MERCHANT("MERCHANT_ID", "商户号"), + PERSON_SUB("PERSONAL_SUB_OPENID", "子商户个人"), + PERSON_PARENT("PERSONAL_OPENID", "父商户个人"), + MANUAL("MANUAL", "手动结算"); + + private final String code; + private final String desc; + + ProfitSharingWxPayType(String code, String desc) { + this.code = code; + this.desc = desc; + } + + public String getCode() { + return code; + } + + public String getDesc() { + return desc; + } +} diff --git a/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingConfigMapper.java b/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingConfigMapper.java new file mode 100644 index 0000000..af3981b --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingConfigMapper.java @@ -0,0 +1,32 @@ +package com.ycwl.basic.profitsharing.mapper; + +import com.ycwl.basic.profitsharing.entity.ProfitSharingConfig; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface ProfitSharingConfigMapper { + @Insert("INSERT INTO profit_sharing_config (scenic_id, name, description, create_time, update_time) " + + "VALUES (#{scenicId}, #{name}, #{description}, #{createTime}, #{updateTime})") + @Options(useGeneratedKeys = true, keyProperty = "id") + void insert(ProfitSharingConfig config); + + @Update("UPDATE profit_sharing_config SET scenic_id = #{scenicId}, name = #{name}, description = #{description}, " + + "create_time = #{createTime}, update_time = #{updateTime} WHERE id = #{id}") + void update(ProfitSharingConfig config); + + @Delete("DELETE FROM profit_sharing_config WHERE id = #{id}") + void deleteById(Long id); + + @Select("SELECT * FROM profit_sharing_config WHERE id = #{id}") + ProfitSharingConfig findById(Long id); + + @Select("SELECT * FROM profit_sharing_config") + List findAll(); + + // 修改: 根据scenicId查询单个记录 + @Select("SELECT * FROM profit_sharing_config WHERE scenic_id = #{scenicId}") + ProfitSharingConfig findByScenicId(Long scenicId); + +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingRecordMapper.java b/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingRecordMapper.java new file mode 100644 index 0000000..4649f37 --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingRecordMapper.java @@ -0,0 +1,17 @@ +package com.ycwl.basic.profitsharing.mapper; + +import com.ycwl.basic.profitsharing.entity.ProfitSharingRecord; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Options; + +import java.util.List; + +@Mapper +public interface ProfitSharingRecordMapper { + void batchInsert(List records); + + @Delete("DELETE FROM profit_sharing_record WHERE scenic_id = #{scenicId} AND order_id = #{orderId}") + void deleteByScenicIdAndOrderId(Long scenicId, Long orderId); +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingUserMapper.java b/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingUserMapper.java new file mode 100644 index 0000000..ba1c100 --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/mapper/ProfitSharingUserMapper.java @@ -0,0 +1,34 @@ +package com.ycwl.basic.profitsharing.mapper; + +import com.ycwl.basic.profitsharing.entity.ProfitSharingUser; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface ProfitSharingUserMapper { + @Insert("INSERT INTO profit_sharing_user (config_id, wx_pay_type, wx_pay_config, name, description, wx_rate, real_rate) " + + "VALUES (#{configId}, #{wxPayType}, #{wxPayConfig}, #{name}, #{description}, #{wxRate}, #{realRate})") + @Options(useGeneratedKeys = true, keyProperty = "id") + void insert(ProfitSharingUser user); + + @Update("UPDATE profit_sharing_user SET config_id = #{configId}, wx_pay_type = #{wxPayType}, wx_pay_config = #{wxPayConfig}, " + + "name = #{name}, description = #{description}, wx_rate = #{wxRate}, real_rate = #{realRate} WHERE id = #{id}") + void update(ProfitSharingUser user); + + @Delete("DELETE FROM profit_sharing_user WHERE id = #{id}") + void deleteById(Long id); + + @Delete("DELETE FROM profit_sharing_user WHERE config_id = #{configId}") + void deleteByConfigId(Long configId); + + @Select("SELECT * FROM profit_sharing_user WHERE id = #{id}") + ProfitSharingUser findById(Long id); + + @Select("SELECT * FROM profit_sharing_user") + List findAll(); + + // 新增: 根据configId查询所有记录 + @Select("SELECT * FROM profit_sharing_user WHERE config_id = #{configId}") + List findByConfigId(Long configId); +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/profitsharing/repository/ProfitSharingRepository.java b/src/main/java/com/ycwl/basic/profitsharing/repository/ProfitSharingRepository.java new file mode 100644 index 0000000..75b741a --- /dev/null +++ b/src/main/java/com/ycwl/basic/profitsharing/repository/ProfitSharingRepository.java @@ -0,0 +1,36 @@ +package com.ycwl.basic.profitsharing.repository; + +import com.alibaba.fastjson.JSONObject; +import com.ycwl.basic.profitsharing.entity.ProfitSharingConfig; +import com.ycwl.basic.profitsharing.mapper.ProfitSharingConfigMapper; +import com.ycwl.basic.profitsharing.mapper.ProfitSharingUserMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class ProfitSharingRepository { + @Autowired + private ProfitSharingConfigMapper configMapper; + @Autowired + private ProfitSharingUserMapper userMapper; + @Autowired + private RedisTemplate redisTemplate; + public static final String PROFIT_SHARING_SCENIC_CONFIG_CACHE_KEY = "profit:scenic:%s"; + + public ProfitSharingConfig getScenicConfig(Long id) { + if (redisTemplate.hasKey(String.format(PROFIT_SHARING_SCENIC_CONFIG_CACHE_KEY, id))) { + return JSONObject.parseObject(redisTemplate.opsForValue().get(String.format(PROFIT_SHARING_SCENIC_CONFIG_CACHE_KEY, id)), ProfitSharingConfig.class); + } + ProfitSharingConfig scenicConfig = configMapper.findByScenicId(id); + if (scenicConfig != null) { + scenicConfig.setUsers(userMapper.findByConfigId(scenicConfig.getId())); + redisTemplate.opsForValue().set(String.format(PROFIT_SHARING_SCENIC_CONFIG_CACHE_KEY, id), JSONObject.toJSONString(scenicConfig)); + } + return scenicConfig; + } + + public void clearScenicConfigCache(Long id) { + redisTemplate.delete(String.format(PROFIT_SHARING_SCENIC_CONFIG_CACHE_KEY, id)); + } +} diff --git a/src/main/resources/mapper/ProfitSharingRecordMapper.xml b/src/main/resources/mapper/ProfitSharingRecordMapper.xml new file mode 100644 index 0000000..b41ed9f --- /dev/null +++ b/src/main/resources/mapper/ProfitSharingRecordMapper.xml @@ -0,0 +1,11 @@ + + + + + INSERT INTO profit_sharing_record (scenic_id, order_id, user_id, user_name, amount, wx_amount, manual_amount, wx_rate, real_rate, order_amount, create_time) + VALUES + + (#{record.scenicId}, #{record.orderId}, #{record.userId}, #{record.userName}, #{record.amount}, #{record.wxAmount}, #{record.manualAmount}, #{record.wxRate}, #{record.realRate}, #{record.orderAmount}, #{record.createTime}) + + + \ No newline at end of file