Merge remote-tracking branch 'origin/develop-backend' into feature-backend-stock-service

feature-backend-stock-service
Masaharu.Kato 4 months ago
commit fa5cdd5056
  1. 7
      backend/src/main/java/com/example/todoapp/controller/ToBuysController.java
  2. 25
      backend/src/main/java/com/example/todoapp/dto/AddByRecipeDTO.java
  3. 11
      backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java
  4. 41
      backend/src/main/java/com/example/todoapp/service/ToBuysService.java
  5. 12
      frontend/src/components/Layout.tsx
  6. 38
      frontend/src/pages/AddRecipe.tsx
  7. 2
      frontend/src/pages/RecipeList.tsx
  8. 4
      frontend/src/services/api.ts

@ -7,6 +7,7 @@
package com.example.todoapp.controller;
import com.example.todoapp.dto.AddByRecipeDTO;
import com.example.todoapp.dto.BuyRequestDTO;
import com.example.todoapp.dto.DeleteToBuyRequestDTO;
import com.example.todoapp.dto.ToBuyResponseDTO;
@ -181,12 +182,10 @@ public class ToBuysController {
*/
@PostMapping("/addByRecipe")
public ResponseEntity<Map<String, Object>> addByRecipe(
@RequestBody Map<String, Long> payload,
@RequestBody AddByRecipeDTO addByRecipeDTO,
Authentication authentication) {
Long recipeId = payload.get("recipeId");
Long servings = payload.get("servings");
List<ToBuyResponseDTO> responseList = toBuysService.addByRecipeId(recipeId, servings,authentication);
List<ToBuyResponseDTO> responseList = toBuysService.addByRecipeId(addByRecipeDTO.getRecipeId(), addByRecipeDTO.getServings(), addByRecipeDTO.getDifference(), authentication);
//shopのフィールドを削除
List<Map<String, Object>> filteredList = responseList.stream()

@ -0,0 +1,25 @@
//--------------------------------
// AddByRecipeDTO.java
//
//
// 更新履歴:2025/06/18 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.dto;
import lombok.Data;
/**
* 料理からの買うものリスト追加データ転送オブジェクトDTO
* <p>
* このクラスはクライアントとサーバー間で料理情報をやり取りするために使用されます
* エンティティとは異なり必要な情報のみを含み関連エンティティへの参照ではなくIDのみを保持します
* </p>
*/
@Data
public class AddByRecipeDTO {
private Long recipeId;
private Long servings;
private Boolean difference;
}

@ -47,6 +47,17 @@ public interface ToBuysRepository extends JpaRepository<ToBuys, Integer> {
*/
@Query("SELECT t FROM ToBuys t WHERE t.user.id = :userId ORDER BY t.tobuyId ASC")
List<ToBuys> findByUserIdOrderByTobuyIdAsc(@Param("userId") Long userId);
/**
* 指定された買うものIDに基づいて買うものでの数量を更新
*
* @param tobuyId 買うものID
* @param amount 買うもの数量
* @return 更新された行数
*/
@Modifying
@Query("UPDATE ToBuys t SET t.amount = :amount WHERE t.tobuyId = :tobuyId")
int updateAmountByTobuyId(Long tobuyId, int amount);
/**
* 指定された買うものIDに基づいて買うものリストを削除

@ -63,6 +63,9 @@ public class ToBuysService {
@Autowired
private RecipeStuffsRepository RecipeStuffsRepository;
@Autowired
private StocksService stocksService;
/**
* 購入リストに新しいアイテムを追加する
*
@ -171,6 +174,16 @@ public class ToBuysService {
return toBuysRepository.findByUserIdOrderByTobuyIdAsc(user.getId());
}
/**
* 指定された購入リストIDに基づいて数量を変更する
*
* @param tobuyId 購入リストID
*/
@Transactional
public int updateToBuysAmountByTobuyId(Long tobuyId, int amount) {
return toBuysRepository.updateAmountByTobuyId(tobuyId, amount);
}
/**
* 指定された購入リストIDに基づいて買うものを削除する
*
@ -207,9 +220,17 @@ public class ToBuysService {
stock.setBuyDate(dto.getBuyDate());
stock.setExpDate(dto.getExpDate());
// 買うものリストから削除
// まだ買うべき数量を計算
int remainAmount = Math.max(tobuy.getAmount() - dto.getAmount(), 0);
System.out.println("remainAmount=" + remainAmount);
// 買うものリストから削除または数量変更
System.out.println("tobuy.getTobuyId()=" + tobuy.getTobuyId());
deleteToBuysByTobuyId(tobuy.getTobuyId());
if (remainAmount > 0) {
updateToBuysAmountByTobuyId(tobuy.getTobuyId(), remainAmount);
} else {
deleteToBuysByTobuyId(tobuy.getTobuyId());
}
// データベースに保存
return stocksRepository.save(stock);
@ -222,7 +243,7 @@ public class ToBuysService {
* @param authentication 認証情報
* @return 追加された買うもののリスト
*/
public List<ToBuyResponseDTO> addByRecipeId(Long recipeId, Long servings,Authentication authentication) {
public List<ToBuyResponseDTO> addByRecipeId(Long recipeId, Long servings, Boolean difference, Authentication authentication) {
// ユーザー情報を取得
String username = authentication.getName();
User user = userRepository.findByUsername(username)
@ -236,13 +257,23 @@ public class ToBuysService {
for (RecipeStuffs rs : recipeStuffsList) {
Stuffs stuff = rs.getStuff();
// 材料の数量をサービング数に基づいて計算
// 材料の数量を在庫数とサービング数に基づいて計算
int stockAmount = stocksService.calcAmountByStuffId(user.getId(), stuff.getStuffId());
int requiredAmount = rs.getAmount() * (servings != null ? servings.intValue() : 1);
if (difference) {
// 差分を計算
requiredAmount = requiredAmount - stockAmount;
if (requiredAmount <= 0) {
continue; // 在庫が十分なので追加しない
}
} else {
// 差分を考慮しない場合はrequiredAmountをそのまま使用
}
Optional<ToBuys> existingToBuyOpt = toBuysRepository.findByUserAndStuff(user, stuff);
ToBuys toBuy;
if (existingToBuyOpt.isPresent()) {
if (existingToBuyOpt.isPresent()) { // 既存の「買うもの」がある場合
toBuy = existingToBuyOpt.get();
toBuy.setAmount(toBuy.getAmount() + requiredAmount); // 既存の数量を更新
} else {

@ -41,15 +41,19 @@ const Layout: React.FC = () => {
const getTabIndex = (pathname: string) => {
pathname = pathname.split("/")[1];
console.log(pathname);
switch (pathname) {
case '/stock':
case 'stock':
return 0;
case '/tasks':
case 'tasks':
return 1;
case '/recipeList':
case 'recipeList':
return 2;
case 'addRecipe':
return 2;
default:
return 0;
return -1;
}
};

@ -18,6 +18,8 @@ import {
DialogActions,
TextField,
Button,
Checkbox,
FormControlLabel,
Box,
} from '@mui/material';
import {
@ -27,7 +29,7 @@ import {
import AddStuffAmountDialog from '../components/AddStuffAmountDialog';
import { StuffAndCategoryAndAmount } from '../types/types';
import EditAmountDialog from '../components/EditAmountDialog';
import { recipeApi, toBuyApi } from '../services/api';
import { recipeApi, toBuyApi, stockApi } from '../services/api';
import { useNavigate, useParams } from 'react-router-dom';
import MessageAlert from '../components/MessageAlert';
import { useMessage } from '../components/MessageContext';
@ -60,6 +62,8 @@ const AddRecipe: React.FC = () => {
const [editingItemIdx, setEditingItemIdx] = useState(0);
//削除確認ダイアログの表示状態
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
// チェックボックスが押されたかどうか
const [checked, setChecked] = useState(false);
// エラーメッセージ表示
const { showErrorMessage, showSuccessMessage } = useMessage();
@ -150,8 +154,32 @@ const AddRecipe: React.FC = () => {
console.log("yes2");
return false;
}
await toBuyApi.addByRecipe(recipeId, numOfPeaple);
showSuccessMessage('レシピが保存されて買うものリストに追加されました!');
const finalAddResult = await toBuyApi.addByRecipe(recipeId, numOfPeaple, checked);
console.log(finalAddResult)
// const recipeStuffInfo = (await recipeApi.getById(recipeId)).stuffAndAmountArray;
// const recipeStuffId = recipeStuffInfo.map(item => item.stuffId);
// console.log(recipeStuffId);
// const stockStuff = await stockApi.getStocks();
// const stockedStuffId = stockStuff.filter(stock => recipeStuff.stuffAndAmountArray).map(item => item.id);
// const stockedStuffAmountRecipe = recipeStuff.filter(recipe => (stockStuff.some(stuff => recipe.stuffId === stuff.stuffId)));
// console.log(stockedStuffAmountRecipe)
// const stockedStuffAmountStock = stockStuff.filter(stock => (stockedStuffAmountRecipe.some(stocked => stock.stuffId === stocked.stuffId)));
// recipeStuff.map(item => {
// await Promise.all(stockStuff.map(async stock => {
// if (item.stuffId == stock.stuffId)
// await toBuyApi.updateToBuy({stuffName: item.stuffName,
// amount: item.amount - stock.amount,
// tobuyId: item.tobuyId,
// stuffId: item.stuffId});
// }))
// });
if (finalAddResult.data.length === 0) {
showSuccessMessage('必要な食材が在庫にあったのでリストには追加されませんでした!');
}
else {
showSuccessMessage('レシピが保存されて買うものリストに追加されました!');
}
navigate('/tasks');
}
@ -337,6 +365,10 @@ const AddRecipe: React.FC = () => {
/>
</div>
<FormControlLabel
control={<Checkbox checked={checked} onChange={(e) => setChecked(e.target.checked)} />}
label={<Typography fontSize="clamp(8px, 1.5vw, 14px)"></Typography>}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenNumOfPeapleDialog(false)}></Button>

@ -102,7 +102,7 @@ const RecipeList: React.FC = () => {
fontSize: "32px", left: "50%", transform: 'translateX(-50%)'
}}
color="primary"
onClick={() => navigate('/AddRecipe')}
onClick={() => navigate('/addRecipe')}
>
</Button>

@ -132,11 +132,11 @@ export const toBuyApi = {
},
addByRecipe: async (recipeId: number, servings: number): Promise<any> => {
addByRecipe: async (recipeId: number, servings: number, difference: boolean): Promise<any> => {
const response = await fetch(`${API_BASE_URL}/api/tobuy/addByRecipe`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({ recipeId, servings }),
body: JSON.stringify({ recipeId, servings, difference }),
})
if (!response.ok) {
throw new Error(TOBUY_ERRORS.CREATE_FAILED);

Loading…
Cancel
Save